1 /* 2 * Copyright (c) 2008, 2009, 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 * uitn32_t a_access_mark; 56 * uint16_t a_flags; 57 * uint16_t a_type; 58 * } act_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 (uid == -1) { 213 if ((flags & ACE_OWNER) > 0) 214 who = UnixUserPrincipals.SPECIAL_OWNER; 215 if ((flags & ACE_GROUP) > 0) 216 who = UnixUserPrincipals.SPECIAL_GROUP; 217 if ((flags & ACE_EVERYONE) > 0) 218 who = UnixUserPrincipals.SPECIAL_EVERYONE; 219 if (who == null) 220 throw new AssertionError("ACE who not handled"); 221 } else { 222 // can be gid 223 if ((flags & ACE_IDENTIFIER_GROUP) > 0) 224 who = UnixUserPrincipals.fromGid(uid); 225 else 226 who = UnixUserPrincipals.fromUid(uid); 227 } 228 229 AclEntryType aceType = null; 230 switch (type) { 231 case ACE_ACCESS_ALLOWED_ACE_TYPE: 232 aceType = AclEntryType.ALLOW; 233 break; 234 case ACE_ACCESS_DENIED_ACE_TYPE: 235 aceType = AclEntryType.DENY; 236 break; 237 case ACE_SYSTEM_AUDIT_ACE_TYPE: 238 aceType = AclEntryType.AUDIT; 239 break; 240 case ACE_SYSTEM_ALARM_ACE_TYPE: 241 aceType = AclEntryType.ALARM; 242 break; 243 default: 244 assert false; 245 } 246 247 Set<AclEntryPermission> aceMask = EnumSet.noneOf(AclEntryPermission.class); 248 if ((mask & ACE_READ_DATA) > 0) 249 aceMask.add(AclEntryPermission.READ_DATA); 250 if ((mask & ACE_WRITE_DATA) > 0) 251 aceMask.add(AclEntryPermission.WRITE_DATA); 252 if ((mask & ACE_APPEND_DATA ) > 0) 253 aceMask.add(AclEntryPermission.APPEND_DATA); 254 if ((mask & ACE_READ_NAMED_ATTRS) > 0) 255 aceMask.add(AclEntryPermission.READ_NAMED_ATTRS); 256 if ((mask & ACE_WRITE_NAMED_ATTRS) > 0) 257 aceMask.add(AclEntryPermission.WRITE_NAMED_ATTRS); 258 if ((mask & ACE_EXECUTE) > 0) 259 aceMask.add(AclEntryPermission.EXECUTE); 260 if ((mask & ACE_DELETE_CHILD ) > 0) 261 aceMask.add(AclEntryPermission.DELETE_CHILD); 262 if ((mask & ACE_READ_ATTRIBUTES) > 0) 263 aceMask.add(AclEntryPermission.READ_ATTRIBUTES); 264 if ((mask & ACE_WRITE_ATTRIBUTES) > 0) 265 aceMask.add(AclEntryPermission.WRITE_ATTRIBUTES); 266 if ((mask & ACE_DELETE) > 0) 267 aceMask.add(AclEntryPermission.DELETE); 268 if ((mask & ACE_READ_ACL) > 0) 269 aceMask.add(AclEntryPermission.READ_ACL); 270 if ((mask & ACE_WRITE_ACL) > 0) 271 aceMask.add(AclEntryPermission.WRITE_ACL); 272 if ((mask & ACE_WRITE_OWNER) > 0) 273 aceMask.add(AclEntryPermission.WRITE_OWNER); 274 if ((mask & ACE_SYNCHRONIZE) > 0) 275 aceMask.add(AclEntryPermission.SYNCHRONIZE); 276 277 Set<AclEntryFlag> aceFlags = EnumSet.noneOf(AclEntryFlag.class); 278 if ((flags & ACE_FILE_INHERIT_ACE) > 0) 279 aceFlags.add(AclEntryFlag.FILE_INHERIT); 280 if ((flags & ACE_DIRECTORY_INHERIT_ACE) > 0) 281 aceFlags.add(AclEntryFlag.DIRECTORY_INHERIT); 282 if ((flags & ACE_NO_PROPAGATE_INHERIT_ACE) > 0) 283 aceFlags.add(AclEntryFlag.NO_PROPAGATE_INHERIT); 284 if ((flags & ACE_INHERIT_ONLY_ACE) > 0) 285 aceFlags.add(AclEntryFlag.INHERIT_ONLY); 286 287 // build the ACL entry and add it to the list 288 AclEntry ace = AclEntry.newBuilder() 289 .setType(aceType) 290 .setPrincipal(who) 291 .setPermissions(aceMask).setFlags(aceFlags).build(); 292 acl.add(ace); 293 } 294 295 return acl; 296 } 297 298 // Retrns true if NFSv4 ACLs not enabled on file system 299 private static boolean isAclsEnabled(int fd) { 300 try { 301 long enabled = fpathconf(fd, _PC_ACL_ENABLED); 302 if (enabled == _ACL_ACE_ENABLED) 303 return true; 304 } catch (UnixException x) { 305 } 306 return false; 307 } 308 309 @Override 310 public List<AclEntry> getAcl() 311 throws IOException 312 { 313 // permission check 314 checkAccess(file, true, false); 315 316 // open file (will fail if file is a link and not following links) 317 int fd = file.openForAttributeAccess(followLinks); 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.getPathForExecptionMessage(), 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 = file.openForAttributeAccess(followLinks); 347 try { 348 // SECURITY: need to copy list as can change during processing 349 acl = new ArrayList<AclEntry>(acl); 350 int n = acl.size(); 351 352 long address = unsafe.allocateMemory(SIZEOF_ACE_T * n); 353 try { 354 encode(acl, address); 355 facl(fd, ACE_SETACL, n, address); 356 } catch (UnixException x) { 357 if ((x.errno() == ENOSYS) || !isAclsEnabled(fd)) { 358 throw new FileSystemException(file.getPathForExecptionMessage(), 359 null, x.getMessage() + " (file system does not support NFSv4 ACLs)"); 360 } 361 if (x.errno() == EINVAL && (n < 3)) 362 throw new IOException("ACL must contain at least 3 entries"); 363 x.rethrowAsIOException(file); 364 } finally { 365 unsafe.freeMemory(address); 366 } 367 } finally { 368 close(fd); 369 } 370 } 371 372 @Override 373 public UserPrincipal getOwner() 374 throws IOException 375 { 376 checkAccess(file, true, false); 377 378 try { 379 UnixFileAttributes attrs = 380 UnixFileAttributes.get(file, followLinks); 381 return UnixUserPrincipals.fromUid(attrs.uid()); 382 } catch (UnixException x) { 383 x.rethrowAsIOException(file); 384 return null; // keep compile happy 385 } 386 } 387 388 @Override 389 public void setOwner(UserPrincipal owner) throws IOException { 390 checkAccess(file, true, false); 391 392 if (!(owner instanceof UnixUserPrincipals.User)) 393 throw new ProviderMismatchException(); 394 if (owner instanceof UnixUserPrincipals.Group) 395 throw new IOException("'owner' parameter is a group"); 396 int uid = ((UnixUserPrincipals.User)owner).uid(); 397 398 try { 399 if (followLinks) { 400 lchown(file, uid, -1); 401 } else { 402 chown(file, uid, -1); 403 } 404 } catch (UnixException x) { 405 x.rethrowAsIOException(file); 406 } 407 } 408 }