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             HashSet<AclEntryPermission> aceMask = new HashSet<>();
 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             HashSet<AclEntryFlag> aceFlags = new HashSet<>();
 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 }