1 /*
   2  * Copyright (c) 2008, 2014, 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 /*
  27  * Portions Copyright (c) 2014 IBM Corporation
  28  */
  29 
  30 package sun.nio.fs;
  31 
  32 import java.nio.file.*;
  33 import java.nio.ByteBuffer;
  34 import java.io.IOException;
  35 import java.util.*;
  36 import sun.misc.Unsafe;
  37 
  38 import static sun.nio.fs.UnixConstants.*;
  39 import static sun.nio.fs.AixNativeDispatcher.*;
  40 
  41 /**
  42  * Aix implementation of UserDefinedFileAttributeView using extended attributes.
  43  */
  44 
  45 class AixUserDefinedFileAttributeView
  46     extends AbstractUserDefinedFileAttributeView
  47 {
  48     private static final Unsafe unsafe = Unsafe.getUnsafe();
  49 
  50     // namespace for extended user attributes
  51     private static final String USER_NAMESPACE = "user.";
  52 
  53     // maximum bytes in extended attribute name (includes namespace)
  54     private static final int XATTR_NAME_MAX = 255;
  55 
  56     private byte[] nameAsBytes(UnixPath file, String name) throws IOException {
  57         if (name == null)
  58             throw new NullPointerException("'name' is null");
  59         name = USER_NAMESPACE + name;
  60         byte[] bytes = name.getBytes();
  61         if (bytes.length > XATTR_NAME_MAX) {
  62             throw new FileSystemException(file.getPathForExceptionMessage(),
  63                 null, "'" + name + "' is too big");
  64         }
  65         return bytes;
  66     }
  67 
  68     // Parses buffer as array of NULL-terminated C strings.
  69     private List<String> asList(long address, int size) {
  70         final List<String> list = new ArrayList<String>();
  71         int start = 0;
  72         int pos = 0;
  73         while (pos < size) {
  74             if (unsafe.getByte(address + pos) == 0) {
  75                 int len = pos - start;
  76                 byte[] value = new byte[len];
  77                 unsafe.copyMemory(null, address+start, value,
  78                     Unsafe.ARRAY_BYTE_BASE_OFFSET, len);
  79                 String s = new String(value);
  80                 if (s.startsWith(USER_NAMESPACE)) {
  81                     s = s.substring(USER_NAMESPACE.length());
  82                     list.add(s);
  83                 }
  84                 start = pos + 1;
  85             }
  86             pos++;
  87         }
  88         return list;
  89     }
  90 
  91     private final UnixPath file;
  92     private final boolean followLinks;
  93 
  94     AixUserDefinedFileAttributeView(UnixPath file, boolean followLinks) {
  95         this.file = file;
  96         this.followLinks = followLinks;
  97     }
  98 
  99     @Override
 100     public List<String> list() throws IOException  {
 101         if (System.getSecurityManager() != null)
 102             checkAccess(file.getPathForPermissionCheck(), true, false);
 103 
 104         int fd = file.openForAttributeAccess(followLinks);
 105         NativeBuffer buffer = null;
 106         try {
 107             int size = 1024;
 108             buffer = NativeBuffers.getNativeBuffer(size);
 109             for (;;) {
 110                 try {
 111                     int n = flistxattr(fd, buffer.address(), size);
 112                     List<String> list = asList(buffer.address(), n);
 113                     return Collections.unmodifiableList(list);
 114                 } catch (UnixException x) {
 115                     // allocate larger buffer if required
 116                     if (x.errno() == ERANGE && size < 32*1024) {
 117                         buffer.release();
 118                         size *= 2;
 119                         buffer = null;
 120                         buffer = NativeBuffers.getNativeBuffer(size);
 121                         continue;
 122                     }
 123                     throw new FileSystemException(file.getPathForExceptionMessage(),
 124                         null, "Unable to get list of extended attributes: " +
 125                         x.getMessage());
 126                 }
 127             }
 128         } finally {
 129             if (buffer != null)
 130                 buffer.release();
 131             close(fd);
 132         }
 133     }
 134 
 135     @Override
 136     public int size(String name) throws IOException  {
 137         if (System.getSecurityManager() != null)
 138             checkAccess(file.getPathForPermissionCheck(), true, false);
 139 
 140         int fd = file.openForAttributeAccess(followLinks);
 141         try {
 142             // fgetxattr returns size if called with size==0
 143             return fgetxattr(fd, nameAsBytes(file,name), 0L, 0);
 144         } catch (UnixException x) {
 145             throw new FileSystemException(file.getPathForExceptionMessage(),
 146                 null, "Unable to get size of extended attribute '" + name +
 147                 "': " + x.getMessage());
 148         } finally {
 149             close(fd);
 150         }
 151     }
 152 
 153     @Override
 154     public int read(String name, ByteBuffer dst) throws IOException {
 155         if (System.getSecurityManager() != null)
 156             checkAccess(file.getPathForPermissionCheck(), true, false);
 157 
 158         if (dst.isReadOnly())
 159             throw new IllegalArgumentException("Read-only buffer");
 160         int pos = dst.position();
 161         int lim = dst.limit();
 162         assert (pos <= lim);
 163         int rem = (pos <= lim ? lim - pos : 0);
 164 
 165         NativeBuffer nb;
 166         long address;
 167         if (dst instanceof sun.nio.ch.DirectBuffer) {
 168             nb = null;
 169             address = ((sun.nio.ch.DirectBuffer)dst).address() + pos;
 170         } else {
 171             // substitute with native buffer
 172             nb = NativeBuffers.getNativeBuffer(rem);
 173             address = nb.address();
 174         }
 175 
 176         int fd = file.openForAttributeAccess(followLinks);
 177         try {
 178             try {
 179                 int n = fgetxattr(fd, nameAsBytes(file,name), address, rem);
 180 
 181                 // if remaining is zero then fgetxattr returns the size
 182                 if (rem == 0) {
 183                     if (n > 0)
 184                         throw new UnixException(ERANGE);
 185                     return 0;
 186                 }
 187 
 188                 // copy from buffer into backing array if necessary
 189                 if (nb != null) {
 190                     int off = dst.arrayOffset() + pos + Unsafe.ARRAY_BYTE_BASE_OFFSET;
 191                     unsafe.copyMemory(null, address, dst.array(), off, n);
 192                 }
 193                 dst.position(pos + n);
 194                 return n;
 195             } catch (UnixException x) {
 196                 String msg = (x.errno() == ERANGE) ?
 197                     "Insufficient space in buffer" : x.getMessage();
 198                 throw new FileSystemException(file.getPathForExceptionMessage(),
 199                     null, "Error reading extended attribute '" + name + "': " + msg);
 200             } finally {
 201                 close(fd);
 202             }
 203         } finally {
 204             if (nb != null)
 205                 nb.release();
 206         }
 207     }
 208 
 209     @Override
 210     public int write(String name, ByteBuffer src) throws IOException {
 211         if (System.getSecurityManager() != null)
 212             checkAccess(file.getPathForPermissionCheck(), false, true);
 213 
 214         int pos = src.position();
 215         int lim = src.limit();
 216         assert (pos <= lim);
 217         int rem = (pos <= lim ? lim - pos : 0);
 218 
 219         NativeBuffer nb;
 220         long address;
 221         if (src instanceof sun.nio.ch.DirectBuffer) {
 222             nb = null;
 223             address = ((sun.nio.ch.DirectBuffer)src).address() + pos;
 224         } else {
 225             // substitute with native buffer
 226             nb = NativeBuffers.getNativeBuffer(rem);
 227             address = nb.address();
 228 
 229             if (src.hasArray()) {
 230                 // copy from backing array into buffer
 231                 int off = src.arrayOffset() + pos + Unsafe.ARRAY_BYTE_BASE_OFFSET;
 232                 unsafe.copyMemory(src.array(), off, null, address, rem);
 233             } else {
 234                 // backing array not accessible so transfer via temporary array
 235                 byte[] tmp = new byte[rem];
 236                 src.get(tmp);
 237                 src.position(pos);  // reset position as write may fail
 238                 unsafe.copyMemory(tmp, Unsafe.ARRAY_BYTE_BASE_OFFSET, null,
 239                     address, rem);
 240             }
 241         }
 242 
 243         int fd = file.openForAttributeAccess(followLinks);
 244         try {
 245             try {
 246                 fsetxattr(fd, nameAsBytes(file,name), address, rem);
 247                 src.position(pos + rem);
 248                 return rem;
 249             } catch (UnixException x) {
 250                 throw new FileSystemException(file.getPathForExceptionMessage(),
 251                     null, "Error writing extended attribute '" + name + "': " +
 252                     x.getMessage());
 253             } finally {
 254                 close(fd);
 255             }
 256         } finally {
 257             if (nb != null)
 258                 nb.release();
 259         }
 260     }
 261 
 262     @Override
 263     public void delete(String name) throws IOException {
 264         if (System.getSecurityManager() != null)
 265             checkAccess(file.getPathForPermissionCheck(), false, true);
 266 
 267         int fd = file.openForAttributeAccess(followLinks);
 268         try {
 269             fremovexattr(fd, nameAsBytes(file,name));
 270         } catch (UnixException x) {
 271             throw new FileSystemException(file.getPathForExceptionMessage(),
 272                 null, "Unable to delete extended attribute '" + name + "': " + x.getMessage());
 273         } finally {
 274             close(fd);
 275         }
 276     }
 277 
 278     /**
 279      * Used by copyTo/moveTo to copy extended attributes from source to target.
 280      *
 281      * @param   ofd
 282      *          file descriptor for source file
 283      * @param   nfd
 284      *          file descriptor for target file
 285      */
 286     static void copyExtendedAttributes(int ofd, int nfd) {
 287         NativeBuffer buffer = null;
 288         try {
 289 
 290             // call flistxattr to get list of extended attributes.
 291             int size = 1024;
 292             buffer = NativeBuffers.getNativeBuffer(size);
 293             for (;;) {
 294                 try {
 295                     size = flistxattr(ofd, buffer.address(), size);
 296                     break;
 297                 } catch (UnixException x) {
 298                     // allocate larger buffer if required
 299                     if (x.errno() == ERANGE && size < 32*1024) {
 300                         buffer.release();
 301                         size *= 2;
 302                         buffer = null;
 303                         buffer = NativeBuffers.getNativeBuffer(size);
 304                         continue;
 305                     }
 306 
 307                     // unable to get list of attributes
 308                     return;
 309                 }
 310             }
 311 
 312             // parse buffer as array of NULL-terminated C strings.
 313             long address = buffer.address();
 314             int start = 0;
 315             int pos = 0;
 316             while (pos < size) {
 317                 if (unsafe.getByte(address + pos) == 0) {
 318                     // extract attribute name and copy attribute to target.
 319                     // FIXME: We can avoid needless copying by using address+pos
 320                     // as the address of the name.
 321                     int len = pos - start;
 322                     byte[] name = new byte[len];
 323                     unsafe.copyMemory(null, address+start, name,
 324                         Unsafe.ARRAY_BYTE_BASE_OFFSET, len);
 325                     try {
 326                         copyExtendedAttribute(ofd, name, nfd);
 327                     } catch (UnixException ignore) {
 328                         // ignore
 329                     }
 330                     start = pos + 1;
 331                 }
 332                 pos++;
 333             }
 334 
 335         } finally {
 336             if (buffer != null)
 337                 buffer.release();
 338         }
 339     }
 340 
 341     private static void copyExtendedAttribute(int ofd, byte[] name, int nfd)
 342         throws UnixException
 343     {
 344         int size = fgetxattr(ofd, name, 0L, 0);
 345         NativeBuffer buffer = NativeBuffers.getNativeBuffer(size);
 346         try {
 347             long address = buffer.address();
 348             size = fgetxattr(ofd, name, address, size);
 349             fsetxattr(nfd, name, address, size);
 350         } finally {
 351             buffer.release();
 352         }
 353     }
 354 }