1/*
2 * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.nio.fs;
27
28import java.nio.file.*;
29import java.nio.file.attribute.*;
30import java.util.*;
31import java.io.IOException;
32import jdk.internal.misc.Unsafe;
33
34import static sun.nio.fs.UnixConstants.*;
35import static sun.nio.fs.SolarisConstants.*;
36import static sun.nio.fs.SolarisNativeDispatcher.*;
37
38
39/**
40 * Solaris implementation of AclFileAttributeView with native support for
41 * NFSv4 ACLs on ZFS.
42 */
43
44class SolarisAclFileAttributeView
45    extends AbstractAclFileAttributeView
46{
47    private static final Unsafe unsafe = Unsafe.getUnsafe();
48
49    // Maximum number of entries allowed in an ACL
50    private static final int MAX_ACL_ENTRIES = 1024;
51
52    /**
53     * typedef struct ace {
54     *     uid_t        a_who;
55     *     uint32_t     a_access_mask;
56     *     uint16_t     a_flags;
57     *     uint16_t     a_type;
58     * } ace_t;
59     */
60    private static final short SIZEOF_ACE_T     = 12;
61    private static final short OFFSETOF_UID     = 0;
62    private static final short OFFSETOF_MASK    = 4;
63    private static final short OFFSETOF_FLAGS   = 8;
64    private static final short OFFSETOF_TYPE    = 10;
65
66    private final UnixPath file;
67    private final boolean followLinks;
68
69    SolarisAclFileAttributeView(UnixPath file, boolean followLinks) {
70        this.file = file;
71        this.followLinks = followLinks;
72    }
73
74    /**
75     * Permission checks to access file
76     */
77    private void checkAccess(UnixPath file,
78                             boolean checkRead,
79                             boolean checkWrite)
80    {
81        SecurityManager sm = System.getSecurityManager();
82        if (sm != null) {
83            if (checkRead)
84                file.checkRead();
85            if (checkWrite)
86                file.checkWrite();
87            sm.checkPermission(new RuntimePermission("accessUserInformation"));
88        }
89    }
90
91    /**
92     * Encode the ACL to the given buffer
93     */
94    private static void encode(List<AclEntry> acl, long address) {
95        long offset = address;
96        for (AclEntry ace: acl) {
97            int flags = 0;
98
99            // map UserPrincipal to uid and flags
100            UserPrincipal who = ace.principal();
101            if (!(who instanceof UnixUserPrincipals.User))
102                throw new ProviderMismatchException();
103            UnixUserPrincipals.User user = (UnixUserPrincipals.User)who;
104            int uid;
105            if (user.isSpecial()) {
106                uid = -1;
107                if (who == UnixUserPrincipals.SPECIAL_OWNER)
108                    flags |= ACE_OWNER;
109                else if (who == UnixUserPrincipals.SPECIAL_GROUP)
110                    flags |= (ACE_GROUP | ACE_IDENTIFIER_GROUP);
111                else if (who == UnixUserPrincipals.SPECIAL_EVERYONE)
112                    flags |= ACE_EVERYONE;
113                else
114                    throw new AssertionError("Unable to map special identifier");
115            } else {
116                if (user instanceof UnixUserPrincipals.Group) {
117                    uid = user.gid();
118                    flags |= ACE_IDENTIFIER_GROUP;
119                } else {
120                    uid = user.uid();
121                }
122            }
123
124            // map ACE type
125            int type;
126            switch (ace.type()) {
127                case ALLOW:
128                    type = ACE_ACCESS_ALLOWED_ACE_TYPE;
129                    break;
130                case DENY:
131                    type = ACE_ACCESS_DENIED_ACE_TYPE;
132                    break;
133                case AUDIT:
134                    type = ACE_SYSTEM_AUDIT_ACE_TYPE;
135                    break;
136                case ALARM:
137                    type = ACE_SYSTEM_ALARM_ACE_TYPE;
138                    break;
139                default:
140                    throw new AssertionError("Unable to map ACE type");
141            }
142
143            // map permissions
144            Set<AclEntryPermission> aceMask = ace.permissions();
145            int mask = 0;
146            if (aceMask.contains(AclEntryPermission.READ_DATA))
147                mask |= ACE_READ_DATA;
148            if (aceMask.contains(AclEntryPermission.WRITE_DATA))
149                mask |= ACE_WRITE_DATA;
150            if (aceMask.contains(AclEntryPermission.APPEND_DATA))
151                mask |= ACE_APPEND_DATA;
152            if (aceMask.contains(AclEntryPermission.READ_NAMED_ATTRS))
153                mask |= ACE_READ_NAMED_ATTRS;
154            if (aceMask.contains(AclEntryPermission.WRITE_NAMED_ATTRS))
155                mask |= ACE_WRITE_NAMED_ATTRS;
156            if (aceMask.contains(AclEntryPermission.EXECUTE))
157                mask |= ACE_EXECUTE;
158            if (aceMask.contains(AclEntryPermission.DELETE_CHILD))
159                mask |= ACE_DELETE_CHILD;
160            if (aceMask.contains(AclEntryPermission.READ_ATTRIBUTES))
161                mask |= ACE_READ_ATTRIBUTES;
162            if (aceMask.contains(AclEntryPermission.WRITE_ATTRIBUTES))
163                mask |= ACE_WRITE_ATTRIBUTES;
164            if (aceMask.contains(AclEntryPermission.DELETE))
165                mask |= ACE_DELETE;
166            if (aceMask.contains(AclEntryPermission.READ_ACL))
167                mask |= ACE_READ_ACL;
168            if (aceMask.contains(AclEntryPermission.WRITE_ACL))
169                mask |= ACE_WRITE_ACL;
170            if (aceMask.contains(AclEntryPermission.WRITE_OWNER))
171                mask |= ACE_WRITE_OWNER;
172            if (aceMask.contains(AclEntryPermission.SYNCHRONIZE))
173                mask |= ACE_SYNCHRONIZE;
174
175            // FIXME - it would be desirable to know here if the file is a
176            // directory or not. Solaris returns EINVAL if an ACE has a directory
177            // -only flag and the file is not a directory.
178            Set<AclEntryFlag> aceFlags = ace.flags();
179            if (aceFlags.contains(AclEntryFlag.FILE_INHERIT))
180                flags |= ACE_FILE_INHERIT_ACE;
181            if (aceFlags.contains(AclEntryFlag.DIRECTORY_INHERIT))
182                flags |= ACE_DIRECTORY_INHERIT_ACE;
183            if (aceFlags.contains(AclEntryFlag.NO_PROPAGATE_INHERIT))
184                flags |= ACE_NO_PROPAGATE_INHERIT_ACE;
185            if (aceFlags.contains(AclEntryFlag.INHERIT_ONLY))
186                flags |= ACE_INHERIT_ONLY_ACE;
187
188            unsafe.putInt(offset + OFFSETOF_UID, uid);
189            unsafe.putInt(offset + OFFSETOF_MASK, mask);
190            unsafe.putShort(offset + OFFSETOF_FLAGS, (short)flags);
191            unsafe.putShort(offset + OFFSETOF_TYPE, (short)type);
192
193            offset += SIZEOF_ACE_T;
194        }
195    }
196
197    /**
198     * Decode the buffer, returning an ACL
199     */
200    private static List<AclEntry> decode(long address, int n) {
201        ArrayList<AclEntry> acl = new ArrayList<>(n);
202        for (int i=0; i<n; i++) {
203            long offset = address + i*SIZEOF_ACE_T;
204
205            int uid = unsafe.getInt(offset + OFFSETOF_UID);
206            int mask = unsafe.getInt(offset + OFFSETOF_MASK);
207            int flags = (int)unsafe.getShort(offset + OFFSETOF_FLAGS);
208            int type = (int)unsafe.getShort(offset + OFFSETOF_TYPE);
209
210            // map uid and flags to UserPrincipal
211            UnixUserPrincipals.User who = null;
212            if ((flags & ACE_OWNER) > 0) {
213                who = UnixUserPrincipals.SPECIAL_OWNER;
214            } else if ((flags & ACE_GROUP) > 0) {
215                who = UnixUserPrincipals.SPECIAL_GROUP;
216            } else if ((flags & ACE_EVERYONE) > 0) {
217                who = UnixUserPrincipals.SPECIAL_EVERYONE;
218            } else if ((flags & ACE_IDENTIFIER_GROUP) > 0) {
219                who = UnixUserPrincipals.fromGid(uid);
220            } else {
221                who = UnixUserPrincipals.fromUid(uid);
222            }
223
224            AclEntryType aceType = null;
225            switch (type) {
226                case ACE_ACCESS_ALLOWED_ACE_TYPE:
227                    aceType = AclEntryType.ALLOW;
228                    break;
229                case ACE_ACCESS_DENIED_ACE_TYPE:
230                    aceType = AclEntryType.DENY;
231                    break;
232                case ACE_SYSTEM_AUDIT_ACE_TYPE:
233                    aceType = AclEntryType.AUDIT;
234                    break;
235                case ACE_SYSTEM_ALARM_ACE_TYPE:
236                    aceType = AclEntryType.ALARM;
237                    break;
238                default:
239                    assert false;
240            }
241
242            Set<AclEntryPermission> aceMask = EnumSet.noneOf(AclEntryPermission.class);
243            if ((mask & ACE_READ_DATA) > 0)
244                aceMask.add(AclEntryPermission.READ_DATA);
245            if ((mask & ACE_WRITE_DATA) > 0)
246                aceMask.add(AclEntryPermission.WRITE_DATA);
247            if ((mask & ACE_APPEND_DATA ) > 0)
248                aceMask.add(AclEntryPermission.APPEND_DATA);
249            if ((mask & ACE_READ_NAMED_ATTRS) > 0)
250                aceMask.add(AclEntryPermission.READ_NAMED_ATTRS);
251            if ((mask & ACE_WRITE_NAMED_ATTRS) > 0)
252                aceMask.add(AclEntryPermission.WRITE_NAMED_ATTRS);
253            if ((mask & ACE_EXECUTE) > 0)
254                aceMask.add(AclEntryPermission.EXECUTE);
255            if ((mask & ACE_DELETE_CHILD ) > 0)
256                aceMask.add(AclEntryPermission.DELETE_CHILD);
257            if ((mask & ACE_READ_ATTRIBUTES) > 0)
258                aceMask.add(AclEntryPermission.READ_ATTRIBUTES);
259            if ((mask & ACE_WRITE_ATTRIBUTES) > 0)
260                aceMask.add(AclEntryPermission.WRITE_ATTRIBUTES);
261            if ((mask & ACE_DELETE) > 0)
262                aceMask.add(AclEntryPermission.DELETE);
263            if ((mask & ACE_READ_ACL) > 0)
264                aceMask.add(AclEntryPermission.READ_ACL);
265            if ((mask & ACE_WRITE_ACL) > 0)
266                aceMask.add(AclEntryPermission.WRITE_ACL);
267            if ((mask & ACE_WRITE_OWNER) > 0)
268                aceMask.add(AclEntryPermission.WRITE_OWNER);
269            if ((mask & ACE_SYNCHRONIZE) > 0)
270                aceMask.add(AclEntryPermission.SYNCHRONIZE);
271
272            Set<AclEntryFlag> aceFlags = EnumSet.noneOf(AclEntryFlag.class);
273            if ((flags & ACE_FILE_INHERIT_ACE) > 0)
274                aceFlags.add(AclEntryFlag.FILE_INHERIT);
275            if ((flags & ACE_DIRECTORY_INHERIT_ACE) > 0)
276                aceFlags.add(AclEntryFlag.DIRECTORY_INHERIT);
277            if ((flags & ACE_NO_PROPAGATE_INHERIT_ACE) > 0)
278                aceFlags.add(AclEntryFlag.NO_PROPAGATE_INHERIT);
279            if ((flags & ACE_INHERIT_ONLY_ACE) > 0)
280                aceFlags.add(AclEntryFlag.INHERIT_ONLY);
281
282            // build the ACL entry and add it to the list
283            AclEntry ace = AclEntry.newBuilder()
284                .setType(aceType)
285                .setPrincipal(who)
286                .setPermissions(aceMask).setFlags(aceFlags).build();
287            acl.add(ace);
288        }
289
290        return acl;
291    }
292
293    // Returns true if NFSv4 ACLs not enabled on file system
294    private static boolean isAclsEnabled(int fd) {
295        try {
296            long enabled = fpathconf(fd, _PC_ACL_ENABLED);
297            if (enabled == _ACL_ACE_ENABLED)
298                return true;
299        } catch (UnixException x) {
300        }
301        return false;
302    }
303
304    @Override
305    public List<AclEntry> getAcl()
306        throws IOException
307    {
308        // permission check
309        checkAccess(file, true, false);
310
311        // open file (will fail if file is a link and not following links)
312        int fd = -1;
313        try {
314            fd = file.openForAttributeAccess(followLinks);
315        } catch (UnixException x) {
316            x.rethrowAsIOException(file);
317        }
318        try {
319            long address = unsafe.allocateMemory(SIZEOF_ACE_T * MAX_ACL_ENTRIES);
320            try {
321                // read ACL and decode it
322                int n = facl(fd, ACE_GETACL, MAX_ACL_ENTRIES, address);
323                assert n >= 0;
324                return decode(address, n);
325            } catch (UnixException x) {
326                if ((x.errno() == ENOSYS) || !isAclsEnabled(fd)) {
327                    throw new FileSystemException(file.getPathForExceptionMessage(),
328                        null, x.getMessage() + " (file system does not support NFSv4 ACLs)");
329                }
330                x.rethrowAsIOException(file);
331                return null;    // keep compiler happy
332            } finally {
333                unsafe.freeMemory(address);
334            }
335        } finally {
336            close(fd);
337        }
338    }
339
340    @Override
341    public void setAcl(List<AclEntry> acl) throws IOException {
342        // permission check
343        checkAccess(file, false, true);
344
345        // open file (will fail if file is a link and not following links)
346        int fd = -1;
347        try {
348            fd = file.openForAttributeAccess(followLinks);
349        } catch (UnixException x) {
350            x.rethrowAsIOException(file);
351        }
352        try {
353            // SECURITY: need to copy list as can change during processing
354            acl = new ArrayList<AclEntry>(acl);
355            int n = acl.size();
356
357            long address = unsafe.allocateMemory(SIZEOF_ACE_T * n);
358            try {
359                encode(acl, address);
360                facl(fd, ACE_SETACL, n, address);
361            } catch (UnixException x) {
362                if ((x.errno() == ENOSYS) || !isAclsEnabled(fd)) {
363                    throw new FileSystemException(file.getPathForExceptionMessage(),
364                        null, x.getMessage() + " (file system does not support NFSv4 ACLs)");
365                }
366                if (x.errno() == EINVAL && (n < 3))
367                    throw new IOException("ACL must contain at least 3 entries");
368                x.rethrowAsIOException(file);
369            } finally {
370                unsafe.freeMemory(address);
371            }
372        } finally {
373            close(fd);
374        }
375    }
376
377    @Override
378    public UserPrincipal getOwner()
379        throws IOException
380    {
381        checkAccess(file, true, false);
382
383        try {
384            UnixFileAttributes attrs =
385                UnixFileAttributes.get(file, followLinks);
386            return UnixUserPrincipals.fromUid(attrs.uid());
387        } catch (UnixException x) {
388            x.rethrowAsIOException(file);
389            return null; // keep compile happy
390        }
391    }
392
393    @Override
394    public void setOwner(UserPrincipal owner) throws IOException {
395        checkAccess(file, true, false);
396
397        if (!(owner instanceof UnixUserPrincipals.User))
398            throw new ProviderMismatchException();
399        if (owner instanceof UnixUserPrincipals.Group)
400            throw new IOException("'owner' parameter is a group");
401        int uid = ((UnixUserPrincipals.User)owner).uid();
402
403        try {
404            if (followLinks) {
405                lchown(file, uid, -1);
406            } else {
407                chown(file, uid, -1);
408            }
409        } catch (UnixException x) {
410            x.rethrowAsIOException(file);
411        }
412    }
413}
414