1 /*
2  * Copyright (c) 2008, 2013, 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 
26 package sun.nio.fs;
27 
28 import java.nio.file.*;
29 import java.nio.file.attribute.*;
30 import java.util.*;
31 import java.io.IOException;
32 import sun.misc.Unsafe;
33 
34 import static sun.nio.fs.UnixConstants.*;
35 import static sun.nio.fs.SolarisConstants.*;
36 import static sun.nio.fs.SolarisNativeDispatcher.*;
37 
38 
39 /**
40  * Solaris implementation of AclFileAttributeView with native support for
41  * NFSv4 ACLs on ZFS.
42  */
43 
44 class 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 
SolarisAclFileAttributeView(UnixPath file, boolean followLinks)69     SolarisAclFileAttributeView(UnixPath file, boolean followLinks) {
70         this.file = file;
71         this.followLinks = followLinks;
72     }
73 
74     /**
75      * Permission checks to access file
76      */
checkAccess(UnixPath file, boolean checkRead, boolean checkWrite)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      */
encode(List<AclEntry> acl, long address)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      */
decode(long address, int n)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     // Retrns true if NFSv4 ACLs not enabled on file system
isAclsEnabled(int fd)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
getAcl()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 = file.openForAttributeAccess(followLinks);
313         try {
314             long address = unsafe.allocateMemory(SIZEOF_ACE_T * MAX_ACL_ENTRIES);
315             try {
316                 // read ACL and decode it
317                 int n = facl(fd, ACE_GETACL, MAX_ACL_ENTRIES, address);
318                 assert n >= 0;
319                 return decode(address, n);
320             } catch (UnixException x) {
321                 if ((x.errno() == ENOSYS) || !isAclsEnabled(fd)) {
322                     throw new FileSystemException(file.getPathForExceptionMessage(),
323                         null, x.getMessage() + " (file system does not support NFSv4 ACLs)");
324                 }
325                 x.rethrowAsIOException(file);
326                 return null;    // keep compiler happy
327             } finally {
328                 unsafe.freeMemory(address);
329             }
330         } finally {
331             close(fd);
332         }
333     }
334 
335     @Override
setAcl(List<AclEntry> acl)336     public void setAcl(List<AclEntry> acl) throws IOException {
337         // permission check
338         checkAccess(file, false, true);
339 
340         // open file (will fail if file is a link and not following links)
341         int fd = file.openForAttributeAccess(followLinks);
342         try {
343             // SECURITY: need to copy list as can change during processing
344             acl = new ArrayList<AclEntry>(acl);
345             int n = acl.size();
346 
347             long address = unsafe.allocateMemory(SIZEOF_ACE_T * n);
348             try {
349                 encode(acl, address);
350                 facl(fd, ACE_SETACL, n, address);
351             } catch (UnixException x) {
352                 if ((x.errno() == ENOSYS) || !isAclsEnabled(fd)) {
353                     throw new FileSystemException(file.getPathForExceptionMessage(),
354                         null, x.getMessage() + " (file system does not support NFSv4 ACLs)");
355                 }
356                 if (x.errno() == EINVAL && (n < 3))
357                     throw new IOException("ACL must contain at least 3 entries");
358                 x.rethrowAsIOException(file);
359             } finally {
360                 unsafe.freeMemory(address);
361             }
362         } finally {
363             close(fd);
364         }
365     }
366 
367     @Override
getOwner()368     public UserPrincipal getOwner()
369         throws IOException
370     {
371         checkAccess(file, true, false);
372 
373         try {
374             UnixFileAttributes attrs =
375                 UnixFileAttributes.get(file, followLinks);
376             return UnixUserPrincipals.fromUid(attrs.uid());
377         } catch (UnixException x) {
378             x.rethrowAsIOException(file);
379             return null; // keep compile happy
380         }
381     }
382 
383     @Override
setOwner(UserPrincipal owner)384     public void setOwner(UserPrincipal owner) throws IOException {
385         checkAccess(file, true, false);
386 
387         if (!(owner instanceof UnixUserPrincipals.User))
388             throw new ProviderMismatchException();
389         if (owner instanceof UnixUserPrincipals.Group)
390             throw new IOException("'owner' parameter is a group");
391         int uid = ((UnixUserPrincipals.User)owner).uid();
392 
393         try {
394             if (followLinks) {
395                 lchown(file, uid, -1);
396             } else {
397                 chown(file, uid, -1);
398             }
399         } catch (UnixException x) {
400             x.rethrowAsIOException(file);
401         }
402     }
403 }
404