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 
  29 import java.nio.file.attribute.*;
  30 import java.util.*;
  31 import java.io.IOException;
  32 
  33 import static sun.nio.fs.WindowsNativeDispatcher.*;
  34 import static sun.nio.fs.WindowsConstants.*;
  35 
  36 class WindowsFileAttributeViews {
  37 
  38     private static class Basic extends AbstractBasicFileAttributeView {
  39         final WindowsPath file;
  40         final boolean followLinks;
  41 
  42         Basic(WindowsPath file, boolean followLinks) {
  43             this.file = file;
  44             this.followLinks = followLinks;
  45         }
  46 
  47         @Override
  48         public WindowsFileAttributes readAttributes() throws IOException {
  49             file.checkRead();
  50             try {
  51                 return WindowsFileAttributes.get(file, followLinks);
  52             } catch (WindowsException x) {
  53                 x.rethrowAsIOException(file);
  54                 return null;    // keep compiler happy
  55             }
  56         }
  57 
  58         /**
  59          * Adjusts a Windows time for the FAT epoch.
  60          */
  61         private long adjustForFatEpoch(long time) {
  62             // 1/1/1980 in Windows Time
  63             final long FAT_EPOCH = 119600064000000000L;
  64             if (time != -1L && time < FAT_EPOCH) {
  65                 return FAT_EPOCH;
  66             } else {
  67                 return time;
  68             }
  69         }
  70 
  71         /**
  72          * Parameter values in Windows times.
  73          */
  74         void setFileTimes(long createTime,
  75                           long lastAccessTime,
  76                           long lastWriteTime)
  77             throws IOException
  78         {
  79             long handle = -1L;
  80             try {
  81                 int flags = FILE_FLAG_BACKUP_SEMANTICS;
  82                 if (!followLinks && file.getFileSystem().supportsLinks())
  83                     flags |= FILE_FLAG_OPEN_REPARSE_POINT;
  84 
  85                 handle = CreateFile(file.getPathForWin32Calls(),
  86                                     FILE_WRITE_ATTRIBUTES,
  87                                     (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
  88                                     OPEN_EXISTING,
  89                                     flags);
  90             } catch (WindowsException x) {
  91                 x.rethrowAsIOException(file);
  92             }
  93 
  94             // update times
  95             try {
  96                 SetFileTime(handle,
  97                             createTime,
  98                             lastAccessTime,
  99                             lastWriteTime);
 100             } catch (WindowsException x) {
 101                 // If ERROR_INVALID_PARAMATER is returned and the volume is
 102                 // FAT then adjust to the FAT epoch and retry.
 103                 if (followLinks && x.lastError() == ERROR_INVALID_PARAMATER) {
 104                     try {
 105                         if (WindowsFileStore.create(file).type().equals("FAT")) {
 106                             SetFileTime(handle,
 107                                         adjustForFatEpoch(createTime),
 108                                         adjustForFatEpoch(lastAccessTime),
 109                                         adjustForFatEpoch(lastWriteTime));
 110                             // retry succeeded
 111                             x = null;
 112                         }
 113                     } catch (SecurityException ignore) {
 114                     } catch (WindowsException ignore) {
 115                     } catch (IOException ignore) {
 116                         // ignore exceptions to let original exception be thrown
 117                     }
 118                 }
 119                 if (x != null)
 120                     x.rethrowAsIOException(file);
 121             } finally {
 122                 CloseHandle(handle);
 123             }
 124         }
 125 
 126         @Override
 127         public void setTimes(FileTime lastModifiedTime,
 128                              FileTime lastAccessTime,
 129                              FileTime createTime) throws IOException
 130         {
 131             // if all null then do nothing
 132             if (lastModifiedTime == null && lastAccessTime == null &&
 133                 createTime == null)
 134             {
 135                 // no effect
 136                 return;
 137             }
 138 
 139             // permission check
 140             file.checkWrite();
 141 
 142             // update times
 143             long t1 = (createTime == null) ? -1L :
 144                 WindowsFileAttributes.toWindowsTime(createTime);
 145             long t2 = (lastAccessTime == null) ? -1L :
 146                 WindowsFileAttributes.toWindowsTime(lastAccessTime);
 147             long t3 = (lastModifiedTime == null) ? -1L :
 148                 WindowsFileAttributes.toWindowsTime(lastModifiedTime);
 149             setFileTimes(t1, t2, t3);
 150         }
 151     }
 152 
 153     static class Dos extends Basic implements DosFileAttributeView {
 154         private static final String READONLY_NAME = "readonly";
 155         private static final String ARCHIVE_NAME = "archive";
 156         private static final String SYSTEM_NAME = "system";
 157         private static final String HIDDEN_NAME = "hidden";
 158         private static final String ATTRIBUTES_NAME = "attributes";
 159 
 160         // the names of the DOS attribtues (includes basic)
 161         static final Set<String> dosAttributeNames =
 162             Util.newSet(basicAttributeNames,
 163                         READONLY_NAME, ARCHIVE_NAME, SYSTEM_NAME,  HIDDEN_NAME, ATTRIBUTES_NAME);
 164 
 165         Dos(WindowsPath file, boolean followLinks) {
 166             super(file, followLinks);
 167         }
 168 
 169         @Override
 170         public String name() {
 171             return "dos";
 172         }
 173 
 174         @Override
 175         public void setAttribute(String attribute, Object value)
 176             throws IOException
 177         {
 178             if (attribute.equals(READONLY_NAME)) {
 179                 setReadOnly((Boolean)value);
 180                 return;
 181             }
 182             if (attribute.equals(ARCHIVE_NAME)) {
 183                 setArchive((Boolean)value);
 184                 return;
 185             }
 186             if (attribute.equals(SYSTEM_NAME)) {
 187                 setSystem((Boolean)value);
 188                 return;
 189             }
 190             if (attribute.equals(HIDDEN_NAME)) {
 191                 setHidden((Boolean)value);
 192                 return;
 193             }
 194             super.setAttribute(attribute, value);
 195         }
 196 
 197         @Override
 198         public Map<String,Object> readAttributes(String[] attributes)
 199             throws IOException
 200         {
 201             AttributesBuilder builder =
 202                 AttributesBuilder.create(dosAttributeNames, attributes);
 203             WindowsFileAttributes attrs = readAttributes();
 204             addRequestedBasicAttributes(attrs, builder);
 205             if (builder.match(READONLY_NAME))
 206                 builder.add(READONLY_NAME, attrs.isReadOnly());
 207             if (builder.match(ARCHIVE_NAME))
 208                 builder.add(ARCHIVE_NAME, attrs.isArchive());
 209             if (builder.match(SYSTEM_NAME))
 210                 builder.add(SYSTEM_NAME, attrs.isSystem());
 211             if (builder.match(HIDDEN_NAME))
 212                 builder.add(HIDDEN_NAME, attrs.isHidden());
 213             if (builder.match(ATTRIBUTES_NAME))
 214                 builder.add(ATTRIBUTES_NAME, attrs.attributes());
 215             return builder.unmodifiableMap();
 216         }
 217 
 218         /**
 219          * Update DOS attributes
 220          */
 221         private void updateAttributes(int flag, boolean enable)
 222             throws IOException
 223         {
 224             file.checkWrite();
 225 
 226             // GetFileAttribtues & SetFileAttributes do not follow links so when
 227             // following links we need the final target
 228             String path = WindowsLinkSupport.getFinalPath(file, followLinks);
 229             try {
 230                 int oldValue = GetFileAttributes(path);
 231                 int newValue = oldValue;
 232                 if (enable) {
 233                     newValue |= flag;
 234                 } else {
 235                     newValue &= ~flag;
 236                 }
 237                 if (newValue != oldValue) {
 238                     SetFileAttributes(path, newValue);
 239                 }
 240             } catch (WindowsException x) {
 241                 // don't reveal target in exception
 242                 x.rethrowAsIOException(file);
 243             }
 244         }
 245 
 246         @Override
 247         public void setReadOnly(boolean value) throws IOException {
 248             updateAttributes(FILE_ATTRIBUTE_READONLY, value);
 249         }
 250 
 251         @Override
 252         public void setHidden(boolean value) throws IOException {
 253             updateAttributes(FILE_ATTRIBUTE_HIDDEN, value);
 254         }
 255 
 256         @Override
 257         public void setArchive(boolean value) throws IOException {
 258             updateAttributes(FILE_ATTRIBUTE_ARCHIVE, value);
 259         }
 260 
 261         @Override
 262         public void setSystem(boolean value) throws IOException {
 263             updateAttributes(FILE_ATTRIBUTE_SYSTEM, value);
 264         }
 265 
 266         // package-private
 267         // Copy given attributes to the file.
 268         void setAttributes(WindowsFileAttributes attrs)
 269             throws IOException
 270         {
 271             // copy DOS attributes to target
 272             int flags = 0;
 273             if (attrs.isReadOnly()) flags |= FILE_ATTRIBUTE_READONLY;
 274             if (attrs.isHidden()) flags |= FILE_ATTRIBUTE_HIDDEN;
 275             if (attrs.isArchive()) flags |= FILE_ATTRIBUTE_ARCHIVE;
 276             if (attrs.isSystem()) flags |= FILE_ATTRIBUTE_SYSTEM;
 277             updateAttributes(flags, true);
 278 
 279             // copy file times to target - must be done after updating FAT attributes
 280             // as otherwise the last modified time may be wrong.
 281             setFileTimes(
 282                 WindowsFileAttributes.toWindowsTime(attrs.creationTime()),
 283                 WindowsFileAttributes.toWindowsTime(attrs.lastModifiedTime()),
 284                 WindowsFileAttributes.toWindowsTime(attrs.lastAccessTime()));
 285         }
 286     }
 287 
 288     static Basic createBasicView(WindowsPath file, boolean followLinks) {
 289         return new Basic(file, followLinks);
 290     }
 291 
 292     static Dos createDosView(WindowsPath file, boolean followLinks) {
 293         return new Dos(file, followLinks);
 294     }
 295 }