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 }