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