1 /*
   2  * Copyright (c) 2014, 2019, 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 #include "FilePath.h"
  27 
  28 #include <algorithm>
  29 #include <list>
  30 #include <ShellAPI.h>
  31 
  32 bool FilePath::FileExists(const TString FileName) {
  33     bool result = false;
  34     WIN32_FIND_DATA FindFileData;
  35     TString fileName = FixPathForPlatform(FileName);
  36     HANDLE handle = FindFirstFile(fileName.data(), &FindFileData);
  37 
  38     if (handle != INVALID_HANDLE_VALUE) {
  39         if (FILE_ATTRIBUTE_DIRECTORY & FindFileData.dwFileAttributes) {
  40             result = true;
  41         }
  42         else {
  43             result = true;
  44         }
  45 
  46         FindClose(handle);
  47     }
  48     return result;
  49 }
  50 
  51 bool FilePath::DirectoryExists(const TString DirectoryName) {
  52     bool result = false;
  53     WIN32_FIND_DATA FindFileData;
  54     TString directoryName = FixPathForPlatform(DirectoryName);
  55     HANDLE handle = FindFirstFile(directoryName.data(), &FindFileData);
  56 
  57     if (handle != INVALID_HANDLE_VALUE) {
  58         if (FILE_ATTRIBUTE_DIRECTORY & FindFileData.dwFileAttributes) {
  59             result = true;
  60         }
  61 
  62         FindClose(handle);
  63     }
  64     return result;
  65 }
  66 
  67 std::string GetLastErrorAsString() {
  68     // Get the error message, if any.
  69     DWORD errorMessageID = ::GetLastError();
  70 
  71     if (errorMessageID == 0) {
  72         return "No error message has been recorded";
  73     }
  74 
  75     LPSTR messageBuffer = NULL;
  76     size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
  77             | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
  78             NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL,
  79             SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
  80 
  81     std::string message(messageBuffer, size);
  82 
  83     // Free the buffer.
  84     LocalFree(messageBuffer);
  85 
  86     return message;
  87 }
  88 
  89 bool FilePath::DeleteFile(const TString FileName) {
  90     bool result = false;
  91 
  92     if (FileExists(FileName) == true) {
  93         TString lFileName = FixPathForPlatform(FileName);
  94         FileAttributes attributes(lFileName);
  95 
  96         if (attributes.Contains(faReadOnly) == true) {
  97             attributes.Remove(faReadOnly);
  98         }
  99 
 100         result = ::DeleteFile(lFileName.data()) == TRUE;
 101     }
 102 
 103     return result;
 104 }
 105 
 106 bool FilePath::DeleteDirectory(const TString DirectoryName) {
 107     bool result = false;
 108 
 109     if (DirectoryExists(DirectoryName) == true) {
 110         SHFILEOPSTRUCTW fos = {0};
 111         TString directoryName = FixPathForPlatform(DirectoryName);
 112         DynamicBuffer<TCHAR> lDirectoryName(directoryName.size() + 2);
 113         if (lDirectoryName.GetData() == NULL) {
 114             return false;
 115         }
 116         memcpy(lDirectoryName.GetData(), directoryName.data(), (directoryName.size() + 2) * sizeof(TCHAR));
 117         lDirectoryName[directoryName.size() + 1] = NULL;
 118         // Double null terminate for SHFileOperation.
 119 
 120         // Delete the folder and everything inside.
 121         fos.wFunc = FO_DELETE;
 122         fos.pFrom = lDirectoryName.GetData();
 123         fos.fFlags = FOF_NO_UI;
 124         result = SHFileOperation(&fos) == 0;
 125     }
 126 
 127     return result;
 128 }
 129 
 130 TString FilePath::IncludeTrailingSeparator(const TString value) {
 131     TString result = value;
 132 
 133     if (value.size() > 0) {
 134         TString::iterator i = result.end();
 135         i--;
 136 
 137         if (*i != TRAILING_PATHSEPARATOR) {
 138             result += TRAILING_PATHSEPARATOR;
 139         }
 140     }
 141 
 142     return result;
 143 }
 144 
 145 TString FilePath::IncludeTrailingSeparator(const char* value) {
 146     TString lvalue = PlatformString(value).toString();
 147     return IncludeTrailingSeparator(lvalue);
 148 }
 149 
 150 TString FilePath::IncludeTrailingSeparator(const wchar_t* value) {
 151     TString lvalue = PlatformString(value).toString();
 152     return IncludeTrailingSeparator(lvalue);
 153 }
 154 
 155 TString FilePath::ExtractFilePath(TString Path) {
 156     TString result;
 157     size_t slash = Path.find_last_of(TRAILING_PATHSEPARATOR);
 158     if (slash != TString::npos)
 159         result = Path.substr(0, slash);
 160     return result;
 161 }
 162 
 163 TString FilePath::ExtractFileExt(TString Path) {
 164     TString result;
 165     size_t dot = Path.find_last_of('.');
 166 
 167     if (dot != TString::npos) {
 168         result  = Path.substr(dot, Path.size() - dot);
 169     }
 170 
 171     return result;
 172 }
 173 
 174 TString FilePath::ExtractFileName(TString Path) {
 175     TString result;
 176 
 177     size_t slash = Path.find_last_of(TRAILING_PATHSEPARATOR);
 178     if (slash != TString::npos)
 179         result = Path.substr(slash + 1, Path.size() - slash - 1);
 180 
 181     return result;
 182 }
 183 
 184 TString FilePath::ChangeFileExt(TString Path, TString Extension) {
 185     TString result;
 186     size_t dot = Path.find_last_of('.');
 187 
 188     if (dot != TString::npos) {
 189         result = Path.substr(0, dot) + Extension;
 190     }
 191 
 192     if (result.empty() == true) {
 193         result = Path;
 194     }
 195 
 196     return result;
 197 }
 198 
 199 TString FilePath::FixPathForPlatform(TString Path) {
 200     TString result = Path;
 201     std::replace(result.begin(), result.end(),
 202             BAD_TRAILING_PATHSEPARATOR, TRAILING_PATHSEPARATOR);
 203     // The maximum path that does not require long path prefix. On Windows the
 204     // maximum path is 260 minus 1 (NUL) but for directories it is 260 minus
 205     // 12 minus 1 (to allow for the creation of a 8.3 file in the directory).
 206     const int maxPath = 247;
 207     if (result.length() > maxPath &&
 208         result.find(_T("\\\\?\\")) == TString::npos &&
 209         result.find(_T("\\\\?\\UNC")) == TString::npos) {
 210         const TString prefix(_T("\\\\"));
 211         if (!result.compare(0, prefix.size(), prefix)) {
 212             // UNC path, converting to UNC path in long notation
 213             result = _T("\\\\?\\UNC") + result.substr(1, result.length());
 214         } else {
 215             // converting to non-UNC path in long notation
 216             result = _T("\\\\?\\") + result;
 217         }
 218     }
 219     return result;
 220 }
 221 
 222 TString FilePath::FixPathSeparatorForPlatform(TString Path) {
 223     TString result = Path;
 224     std::replace(result.begin(), result.end(),
 225             BAD_PATH_SEPARATOR, PATH_SEPARATOR);
 226     return result;
 227 }
 228 
 229 TString FilePath::PathSeparator() {
 230     TString result;
 231     result = PATH_SEPARATOR;
 232     return result;
 233 }
 234 
 235 bool FilePath::CreateDirectory(TString Path, bool ownerOnly) {
 236     bool result = false;
 237 
 238     std::list<TString> paths;
 239     TString lpath = Path;
 240 
 241     while (lpath.empty() == false && DirectoryExists(lpath) == false) {
 242         paths.push_front(lpath);
 243         lpath = ExtractFilePath(lpath);
 244     }
 245 
 246     for (std::list<TString>::iterator iterator = paths.begin();
 247             iterator != paths.end(); iterator++) {
 248         lpath = *iterator;
 249 
 250         if (_wmkdir(lpath.data()) == 0) {
 251             result = true;
 252         } else {
 253             result = false;
 254             break;
 255         }
 256     }
 257 
 258     return result;
 259 }
 260 
 261 void FilePath::ChangePermissions(TString FileName, bool ownerOnly) {
 262 }
 263 
 264 #include <algorithm>
 265 
 266 FileAttributes::FileAttributes(const TString FileName, bool FollowLink) {
 267     FFileName = FileName;
 268     FFollowLink = FollowLink;
 269     ReadAttributes();
 270 }
 271 
 272 bool FileAttributes::WriteAttributes() {
 273     bool result = false;
 274 
 275     DWORD attributes = 0;
 276 
 277     for (std::vector<FileAttribute>::const_iterator iterator =
 278             FAttributes.begin();
 279         iterator != FAttributes.end(); iterator++) {
 280         switch (*iterator) {
 281             case faArchive: {
 282                 attributes = attributes & FILE_ATTRIBUTE_ARCHIVE;
 283                 break;
 284             }
 285             case faCompressed: {
 286                 attributes = attributes & FILE_ATTRIBUTE_COMPRESSED;
 287                 break;
 288             }
 289             case faDevice: {
 290                 attributes = attributes & FILE_ATTRIBUTE_DEVICE;
 291                 break;
 292             }
 293             case faDirectory: {
 294                 attributes = attributes & FILE_ATTRIBUTE_DIRECTORY;
 295                 break;
 296             }
 297             case faEncrypted: {
 298                 attributes = attributes & FILE_ATTRIBUTE_ENCRYPTED;
 299                 break;
 300             }
 301             case faHidden: {
 302                 attributes = attributes & FILE_ATTRIBUTE_HIDDEN;
 303                 break;
 304             }
 305             case faNormal: {
 306                 attributes = attributes & FILE_ATTRIBUTE_NORMAL;
 307                 break;
 308             }
 309             case faNotContentIndexed: {
 310                 attributes = attributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
 311                 break;
 312             }
 313             case faOffline: {
 314                 attributes = attributes & FILE_ATTRIBUTE_OFFLINE;
 315                 break;
 316             }
 317             case faSystem: {
 318                 attributes = attributes & FILE_ATTRIBUTE_SYSTEM;
 319                 break;
 320             }
 321             case faSymbolicLink: {
 322                 attributes = attributes & FILE_ATTRIBUTE_REPARSE_POINT;
 323                 break;
 324             }
 325             case faSparceFile: {
 326                 attributes = attributes & FILE_ATTRIBUTE_SPARSE_FILE;
 327                 break;
 328             }
 329             case faReadOnly: {
 330                 attributes = attributes & FILE_ATTRIBUTE_READONLY;
 331                 break;
 332             }
 333             case faTemporary: {
 334                 attributes = attributes & FILE_ATTRIBUTE_TEMPORARY;
 335                 break;
 336             }
 337             case faVirtual: {
 338                 attributes = attributes & FILE_ATTRIBUTE_VIRTUAL;
 339                 break;
 340             }
 341         }
 342     }
 343 
 344     if (::SetFileAttributes(FFileName.data(), attributes) != 0) {
 345         result = true;
 346     }
 347 
 348     return result;
 349 }
 350 
 351 #define S_ISRUSR(m)    (((m) & S_IRWXU) == S_IRUSR)
 352 #define S_ISWUSR(m)    (((m) & S_IRWXU) == S_IWUSR)
 353 #define S_ISXUSR(m)    (((m) & S_IRWXU) == S_IXUSR)
 354 
 355 #define S_ISRGRP(m)    (((m) & S_IRWXG) == S_IRGRP)
 356 #define S_ISWGRP(m)    (((m) & S_IRWXG) == S_IWGRP)
 357 #define S_ISXGRP(m)    (((m) & S_IRWXG) == S_IXGRP)
 358 
 359 #define S_ISROTH(m)    (((m) & S_IRWXO) == S_IROTH)
 360 #define S_ISWOTH(m)    (((m) & S_IRWXO) == S_IWOTH)
 361 #define S_ISXOTH(m)    (((m) & S_IRWXO) == S_IXOTH)
 362 
 363 bool FileAttributes::ReadAttributes() {
 364     bool result = false;
 365 
 366     DWORD attributes = ::GetFileAttributes(FFileName.data());
 367 
 368     if (attributes != INVALID_FILE_ATTRIBUTES) {
 369         result = true;
 370 
 371         if (attributes | FILE_ATTRIBUTE_ARCHIVE) {
 372             FAttributes.push_back(faArchive);
 373         }
 374         if (attributes | FILE_ATTRIBUTE_COMPRESSED) {
 375             FAttributes.push_back(faCompressed);
 376         }
 377         if (attributes | FILE_ATTRIBUTE_DEVICE) {
 378             FAttributes.push_back(faDevice);
 379         }
 380         if (attributes | FILE_ATTRIBUTE_DIRECTORY) {
 381             FAttributes.push_back(faDirectory);
 382         }
 383         if (attributes | FILE_ATTRIBUTE_ENCRYPTED) {
 384             FAttributes.push_back(faEncrypted);
 385         }
 386         if (attributes | FILE_ATTRIBUTE_HIDDEN) {
 387             FAttributes.push_back(faHidden);
 388         }
 389         // if (attributes | FILE_ATTRIBUTE_INTEGRITY_STREAM) {
 390         //     FAttributes.push_back(faIntegrityStream);
 391         // }
 392         if (attributes | FILE_ATTRIBUTE_NORMAL) {
 393             FAttributes.push_back(faNormal);
 394         }
 395         if (attributes | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) {
 396             FAttributes.push_back(faNotContentIndexed);
 397         }
 398         // if (attributes | FILE_ATTRIBUTE_NO_SCRUB_DATA) {
 399         //     FAttributes.push_back(faNoScrubData);
 400         // }
 401         if (attributes | FILE_ATTRIBUTE_SYSTEM) {
 402             FAttributes.push_back(faSystem);
 403         }
 404         if (attributes | FILE_ATTRIBUTE_OFFLINE) {
 405             FAttributes.push_back(faOffline);
 406         }
 407         if (attributes | FILE_ATTRIBUTE_REPARSE_POINT) {
 408             FAttributes.push_back(faSymbolicLink);
 409         }
 410         if (attributes | FILE_ATTRIBUTE_SPARSE_FILE) {
 411             FAttributes.push_back(faSparceFile);
 412         }
 413         if (attributes | FILE_ATTRIBUTE_READONLY ) {
 414             FAttributes.push_back(faReadOnly);
 415         }
 416         if (attributes | FILE_ATTRIBUTE_TEMPORARY) {
 417             FAttributes.push_back(faTemporary);
 418         }
 419         if (attributes | FILE_ATTRIBUTE_VIRTUAL) {
 420             FAttributes.push_back(faVirtual);
 421         }
 422     }
 423 
 424     return result;
 425 }
 426 
 427 bool FileAttributes::Valid(const FileAttribute Value) {
 428     bool result = false;
 429 
 430     switch (Value) {
 431         case faHidden:
 432         case faReadOnly: {
 433             result = true;
 434             break;
 435         }
 436         default:
 437             break;
 438     }
 439 
 440     return result;
 441 }
 442 
 443 void FileAttributes::Append(FileAttribute Value) {
 444     if (Valid(Value) == true) {
 445         FAttributes.push_back(Value);
 446         WriteAttributes();
 447     }
 448 }
 449 
 450 bool FileAttributes::Contains(FileAttribute Value) {
 451     bool result = false;
 452 
 453     std::vector<FileAttribute>::const_iterator iterator =
 454             std::find(FAttributes.begin(), FAttributes.end(), Value);
 455 
 456     if (iterator != FAttributes.end()) {
 457         result = true;
 458     }
 459 
 460     return result;
 461 }
 462 
 463 void FileAttributes::Remove(FileAttribute Value) {
 464     if (Valid(Value) == true) {
 465         std::vector<FileAttribute>::iterator iterator =
 466             std::find(FAttributes.begin(), FAttributes.end(), Value);
 467 
 468         if (iterator != FAttributes.end()) {
 469             FAttributes.erase(iterator);
 470             WriteAttributes();
 471         }
 472     }
 473 }