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