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.attribute.*;
     29 import java.util.Map;
     30 import java.util.Set;
     31 import java.io.IOException;
     32 import sun.misc.Unsafe;
     33 
     34 import static sun.nio.fs.UnixNativeDispatcher.*;
     35 import static sun.nio.fs.UnixConstants.*;
     36 
     37 /**
     38  * Linux implementation of DosFileAttributeView for use on file systems such
     39  * as ext3 that have extended attributes enabled and SAMBA configured to store
     40  * DOS attributes.
     41  */
     42 
     43 class LinuxDosFileAttributeView
     44     extends UnixFileAttributeViews.Basic implements DosFileAttributeView
     45 {
     46     private static final Unsafe unsafe = Unsafe.getUnsafe();
     47 
     48     private static final String READONLY_NAME = "readonly";
     49     private static final String ARCHIVE_NAME = "archive";
     50     private static final String SYSTEM_NAME = "system";
     51     private static final String HIDDEN_NAME = "hidden";
     52 
     53     private static final String DOS_XATTR_NAME = "user.DOSATTRIB";
     54     private static final byte[] DOS_XATTR_NAME_AS_BYTES = Util.toBytes(DOS_XATTR_NAME);
     55 
     56     private static final int DOS_XATTR_READONLY = 0x01;
     57     private static final int DOS_XATTR_HIDDEN   = 0x02;
     58     private static final int DOS_XATTR_SYSTEM   = 0x04;
     59     private static final int DOS_XATTR_ARCHIVE  = 0x20;
     60 
     61     // the names of the DOS attributes (includes basic)
     62     private static final Set<String> dosAttributeNames =
     63         Util.newSet(basicAttributeNames, READONLY_NAME, ARCHIVE_NAME, SYSTEM_NAME, HIDDEN_NAME);
     64 
     65     LinuxDosFileAttributeView(UnixPath file, boolean followLinks) {
     66         super(file, followLinks);
     67     }
     68 
     69     @Override
     70     public String name() {
     71         return "dos";
     72     }
     73 
     74     @Override
     75     public void setAttribute(String attribute, Object value)
     76         throws IOException
     77     {
     78         if (attribute.equals(READONLY_NAME)) {
     79             setReadOnly((Boolean)value);
     80             return;
     81         }
     82         if (attribute.equals(ARCHIVE_NAME)) {
     83             setArchive((Boolean)value);
     84             return;
     85         }
     86         if (attribute.equals(SYSTEM_NAME)) {
     87             setSystem((Boolean)value);
     88             return;
     89         }
     90         if (attribute.equals(HIDDEN_NAME)) {
     91             setHidden((Boolean)value);
     92             return;
     93         }
     94         super.setAttribute(attribute, value);
     95     }
     96 
     97     @Override
     98     public Map<String,Object> readAttributes(String[] attributes)
     99         throws IOException
    100     {
    101         AttributesBuilder builder =
    102             AttributesBuilder.create(dosAttributeNames, attributes);
    103         DosFileAttributes attrs = readAttributes();
    104         addRequestedBasicAttributes(attrs, builder);
    105         if (builder.match(READONLY_NAME))
    106             builder.add(READONLY_NAME, attrs.isReadOnly());
    107         if (builder.match(ARCHIVE_NAME))
    108             builder.add(ARCHIVE_NAME, attrs.isArchive());
    109         if (builder.match(SYSTEM_NAME))
    110             builder.add(SYSTEM_NAME, attrs.isSystem());
    111         if (builder.match(HIDDEN_NAME))
    112             builder.add(HIDDEN_NAME, attrs.isHidden());
    113         return builder.unmodifiableMap();
    114     }
    115 
    116     @Override
    117     public DosFileAttributes readAttributes() throws IOException {
    118         file.checkRead();
    119 
    120         int fd = file.openForAttributeAccess(followLinks);
    121         try {
    122              final UnixFileAttributes attrs = UnixFileAttributes.get(fd);
    123              final int dosAttribute = getDosAttribute(fd);
    124 
    125              return new DosFileAttributes() {
    126                 @Override
    127                 public FileTime lastModifiedTime() {
    128                     return attrs.lastModifiedTime();
    129                 }
    130                 @Override
    131                 public FileTime lastAccessTime() {
    132                     return attrs.lastAccessTime();
    133                 }
    134                 @Override
    135                 public FileTime creationTime() {
    136                     return attrs.creationTime();
    137                 }
    138                 @Override
    139                 public boolean isRegularFile() {
    140                     return attrs.isRegularFile();
    141                 }
    142                 @Override
    143                 public boolean isDirectory() {
    144                     return attrs.isDirectory();
    145                 }
    146                 @Override
    147                 public boolean isSymbolicLink() {
    148                     return attrs.isSymbolicLink();
    149                 }
    150                 @Override
    151                 public boolean isOther() {
    152                     return attrs.isOther();
    153                 }
    154                 @Override
    155                 public long size() {
    156                     return attrs.size();
    157                 }
    158                 @Override
    159                 public Object fileKey() {
    160                     return attrs.fileKey();
    161                 }
    162                 @Override
    163                 public boolean isReadOnly() {
    164                     return (dosAttribute & DOS_XATTR_READONLY) != 0;
    165                 }
    166                 @Override
    167                 public boolean isHidden() {
    168                     return (dosAttribute & DOS_XATTR_HIDDEN) != 0;
    169                 }
    170                 @Override
    171                 public boolean isArchive() {
    172                     return (dosAttribute & DOS_XATTR_ARCHIVE) != 0;
    173                 }
    174                 @Override
    175                 public boolean isSystem() {
    176                     return (dosAttribute & DOS_XATTR_SYSTEM) != 0;
    177                 }
    178              };
    179 
    180         } catch (UnixException x) {
    181             x.rethrowAsIOException(file);
    182             return null;    // keep compiler happy
    183         } finally {
    184             close(fd);
    185         }
    186     }
    187 
    188     @Override
    189     public void setReadOnly(boolean value) throws IOException {
    190         updateDosAttribute(DOS_XATTR_READONLY, value);
    191     }
    192 
    193     @Override
    194     public void setHidden(boolean value) throws IOException {
    195         updateDosAttribute(DOS_XATTR_HIDDEN, value);
    196     }
    197 
    198     @Override
    199     public void setArchive(boolean value) throws IOException {
    200         updateDosAttribute(DOS_XATTR_ARCHIVE, value);
    201     }
    202 
    203     @Override
    204     public void setSystem(boolean value) throws IOException {
    205         updateDosAttribute(DOS_XATTR_SYSTEM, value);
    206     }
    207 
    208     /**
    209      * Reads the value of the user.DOSATTRIB extended attribute
    210      */
    211     private int getDosAttribute(int fd) throws UnixException {
    212         final int size = 24;
    213 
    214         NativeBuffer buffer = NativeBuffers.getNativeBuffer(size);
    215         try {
    216             int len = LinuxNativeDispatcher
    217                 .fgetxattr(fd, DOS_XATTR_NAME_AS_BYTES, buffer.address(), size);
    218 
    219             if (len > 0) {
    220                 // ignore null terminator
    221                 if (unsafe.getByte(buffer.address()+len-1) == 0)
    222                     len--;
    223 
    224                 // convert to String and parse
    225                 byte[] buf = new byte[len];
    226                 // Android-changed: We don't have Unsafe.copyMemory yet, so we use putByte.
    227                 // unsafe.copyMemory(null, buffer.address(), buf,
    228                 //     Unsafe.ARRAY_BYTE_BASE_OFFSET, len);
    229                 for (int i = 0; i < len; i++) {
    230                     buf[i] = unsafe.getByte(buffer.address() + i);
    231                 }
    232                 String value = Util.toString(buf);
    233 
    234                 // should be something like 0x20
    235                 if (value.length() >= 3 && value.startsWith("0x")) {
    236                     try {
    237                         return Integer.parseInt(value.substring(2), 16);
    238                     } catch (NumberFormatException x) {
    239                         // ignore
    240                     }
    241                 }
    242             }
    243             throw new UnixException("Value of " + DOS_XATTR_NAME + " attribute is invalid");
    244         } catch (UnixException x) {
    245             // default value when attribute does not exist
    246             if (x.errno() == ENODATA)
    247                 return 0;
    248             throw x;
    249         } finally {
    250             buffer.release();
    251         }
    252     }
    253 
    254     /**
    255      * Updates the value of the user.DOSATTRIB extended attribute
    256      */
    257     private void updateDosAttribute(int flag, boolean enable) throws IOException {
    258         file.checkWrite();
    259 
    260         int fd = file.openForAttributeAccess(followLinks);
    261         try {
    262             int oldValue = getDosAttribute(fd);
    263             int newValue = oldValue;
    264             if (enable) {
    265                 newValue |= flag;
    266             } else {
    267                 newValue &= ~flag;
    268             }
    269             if (newValue != oldValue) {
    270                 byte[] value = Util.toBytes("0x" + Integer.toHexString(newValue));
    271                 NativeBuffer buffer = NativeBuffers.asNativeBuffer(value);
    272                 try {
    273                     LinuxNativeDispatcher.fsetxattr(fd, DOS_XATTR_NAME_AS_BYTES,
    274                         buffer.address(), value.length+1);
    275                 } finally {
    276                     buffer.release();
    277                 }
    278             }
    279         } catch (UnixException x) {
    280             x.rethrowAsIOException(file);
    281         } finally {
    282             close(fd);
    283         }
    284     }
    285 }
    286