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 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 = 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 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 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 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 }