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(),
 117                 (directoryName.size() + 2) * sizeof(TCHAR));
 118         lDirectoryName[directoryName.size() + 1] = NULL;
 119         // Double null terminate for SHFileOperation.
 120 
 121         // Delete the folder and everything inside.
 122         fos.wFunc = FO_DELETE;
 123         fos.pFrom = lDirectoryName.GetData();
 124         fos.fFlags = FOF_NO_UI;
 125         result = SHFileOperation(&fos) == 0;
 126     }
 127 
 128     return result;
 129 }
 130 
 131 TString FilePath::IncludeTrailingSeparator(const TString value) {
 132     TString result = value;
 133 
 134     if (value.size() > 0) {
 135         TString::iterator i = result.end();
 136         i--;
 137 
 138         if (*i != TRAILING_PATHSEPARATOR) {
 139             result += TRAILING_PATHSEPARATOR;
 140         }
 141     }
 142 
 143     return result;
 144 }
 145 
 146 TString FilePath::IncludeTrailingSeparator(const char* value) {
 147     TString lvalue = PlatformString(value).toString();
 148     return IncludeTrailingSeparator(lvalue);
 149 }
 150 
 151 TString FilePath::IncludeTrailingSeparator(const wchar_t* value) {
 152     TString lvalue = PlatformString(value).toString();
 153     return IncludeTrailingSeparator(lvalue);
 154 }
 155 
 156 TString FilePath::ExtractFilePath(TString Path) {
 157     TString result;
 158     size_t slash = Path.find_last_of(TRAILING_PATHSEPARATOR);
 159     if (slash != TString::npos)
 160         result = Path.substr(0, slash);
 161     return result;
 162 }
 163 
 164 TString FilePath::ExtractFileExt(TString Path) {
 165     TString result;
 166     size_t dot = Path.find_last_of('.');
 167 
 168     if (dot != TString::npos) {
 169         result  = Path.substr(dot, Path.size() - dot);
 170     }
 171 
 172     return result;
 173 }
 174 
 175 TString FilePath::ExtractFileName(TString Path) {
 176     TString result;
 177 
 178     size_t slash = Path.find_last_of(TRAILING_PATHSEPARATOR);
 179     if (slash != TString::npos)
 180         result = Path.substr(slash + 1, Path.size() - slash - 1);
 181 
 182     return result;
 183 }
 184 
 185 TString FilePath::ChangeFileExt(TString Path, TString Extension) {
 186     TString result;
 187     size_t dot = Path.find_last_of('.');
 188 
 189     if (dot != TString::npos) {
 190         result = Path.substr(0, dot) + Extension;
 191     }
 192 
 193     if (result.empty() == true) {
 194         result = Path;
 195     }
 196 
 197     return result;
 198 }
 199 
 200 TString FilePath::FixPathForPlatform(TString Path) {
 201     TString result = Path;
 202     std::replace(result.begin(), result.end(),
 203             BAD_TRAILING_PATHSEPARATOR, TRAILING_PATHSEPARATOR);
 204     // The maximum path that does not require long path prefix. On Windows the
 205     // maximum path is 260 minus 1 (NUL) but for directories it is 260 minus
 206     // 12 minus 1 (to allow for the creation of a 8.3 file in the directory).
 207     const int maxPath = 247;
 208     if (result.length() > maxPath &&
 209         result.find(_T("\\\\?\\")) == TString::npos &&
 210         result.find(_T("\\\\?\\UNC")) == TString::npos) {
 211         const TString prefix(_T("\\\\"));
 212         if (!result.compare(0, prefix.size(), prefix)) {
 213             // UNC path, converting to UNC path in long notation
 214             result = _T("\\\\?\\UNC") + result.substr(1, result.length());
 215         } else {
 216             // converting to non-UNC path in long notation
 217             result = _T("\\\\?\\") + result;
 218         }
 219     }
 220     return result;
 221 }
 222 
 223 TString FilePath::FixPathSeparatorForPlatform(TString Path) {
 224     TString result = Path;
 225     std::replace(result.begin(), result.end(),
 226             BAD_PATH_SEPARATOR, PATH_SEPARATOR);
 227     return result;
 228 }
 229 
 230 TString FilePath::PathSeparator() {
 231     TString result;
 232     result = PATH_SEPARATOR;
 233     return result;
 234 }
 235 
 236 bool FilePath::CreateDirectory(TString Path, bool ownerOnly) {
 237     bool result = false;
 238 
 239     std::list<TString> paths;
 240     TString lpath = Path;
 241 
 242     while (lpath.empty() == false && DirectoryExists(lpath) == false) {
 243         paths.push_front(lpath);
 244         lpath = ExtractFilePath(lpath);
 245     }
 246 
 247     for (std::list<TString>::iterator iterator = paths.begin();
 248             iterator != paths.end(); iterator++) {
 249         lpath = *iterator;
 250 
 251         if (_wmkdir(lpath.data()) == 0) {
 252             result = true;
 253         } else {
 254             result = false;
 255             break;
 256         }
 257     }
 258 
 259     return result;
 260 }
 261 
 262 void FilePath::ChangePermissions(TString FileName, bool ownerOnly) {
 263 }
 264 
 265 #include <algorithm>
 266 
 267 FileAttributes::FileAttributes(const TString FileName, bool FollowLink) {
 268     FFileName = FileName;
 269     FFollowLink = FollowLink;
 270     ReadAttributes();
 271 }
 272 
 273 bool FileAttributes::WriteAttributes() {
 274     bool result = false;
 275 
 276     DWORD attributes = 0;
 277 
 278     for (std::vector<FileAttribute>::const_iterator iterator =
 279             FAttributes.begin();
 280         iterator != FAttributes.end(); iterator++) {
 281         switch (*iterator) {
 282             case faArchive: {
 283                 attributes = attributes & FILE_ATTRIBUTE_ARCHIVE;
 284                 break;
 285             }
 286             case faCompressed: {
 287                 attributes = attributes & FILE_ATTRIBUTE_COMPRESSED;
 288                 break;
 289             }
 290             case faDevice: {
 291                 attributes = attributes & FILE_ATTRIBUTE_DEVICE;
 292                 break;
 293             }
 294             case faDirectory: {
 295                 attributes = attributes & FILE_ATTRIBUTE_DIRECTORY;
 296                 break;
 297             }
 298             case faEncrypted: {
 299                 attributes = attributes & FILE_ATTRIBUTE_ENCRYPTED;
 300                 break;
 301             }
 302             case faHidden: {
 303                 attributes = attributes & FILE_ATTRIBUTE_HIDDEN;
 304                 break;
 305             }
 306             case faNormal: {
 307                 attributes = attributes & FILE_ATTRIBUTE_NORMAL;
 308                 break;
 309             }
 310             case faNotContentIndexed: {
 311                 attributes = attributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
 312                 break;
 313             }
 314             case faOffline: {
 315                 attributes = attributes & FILE_ATTRIBUTE_OFFLINE;
 316                 break;
 317             }
 318             case faSystem: {
 319                 attributes = attributes & FILE_ATTRIBUTE_SYSTEM;
 320                 break;
 321             }
 322             case faSymbolicLink: {
 323                 attributes = attributes & FILE_ATTRIBUTE_REPARSE_POINT;
 324                 break;
 325             }
 326             case faSparceFile: {
 327                 attributes = attributes & FILE_ATTRIBUTE_SPARSE_FILE;
 328                 break;
 329             }
 330             case faReadOnly: {
 331                 attributes = attributes & FILE_ATTRIBUTE_READONLY;
 332                 break;
 333             }
 334             case faTemporary: {
 335                 attributes = attributes & FILE_ATTRIBUTE_TEMPORARY;
 336                 break;
 337             }
 338             case faVirtual: {
 339                 attributes = attributes & FILE_ATTRIBUTE_VIRTUAL;
 340                 break;
 341             }
 342         }
 343     }
 344 
 345     if (::SetFileAttributes(FFileName.data(), attributes) != 0) {
 346         result = true;
 347     }
 348 
 349     return result;
 350 }
 351 
 352 #define S_ISRUSR(m)    (((m) & S_IRWXU) == S_IRUSR)
 353 #define S_ISWUSR(m)    (((m) & S_IRWXU) == S_IWUSR)
 354 #define S_ISXUSR(m)    (((m) & S_IRWXU) == S_IXUSR)
 355 
 356 #define S_ISRGRP(m)    (((m) & S_IRWXG) == S_IRGRP)
 357 #define S_ISWGRP(m)    (((m) & S_IRWXG) == S_IWGRP)
 358 #define S_ISXGRP(m)    (((m) & S_IRWXG) == S_IXGRP)
 359 
 360 #define S_ISROTH(m)    (((m) & S_IRWXO) == S_IROTH)
 361 #define S_ISWOTH(m)    (((m) & S_IRWXO) == S_IWOTH)
 362 #define S_ISXOTH(m)    (((m) & S_IRWXO) == S_IXOTH)
 363 
 364 bool FileAttributes::ReadAttributes() {
 365     bool result = false;
 366 
 367     DWORD attributes = ::GetFileAttributes(FFileName.data());
 368 
 369     if (attributes != INVALID_FILE_ATTRIBUTES) {
 370         result = true;
 371 
 372         if (attributes | FILE_ATTRIBUTE_ARCHIVE) {
 373             FAttributes.push_back(faArchive);
 374         }
 375         if (attributes | FILE_ATTRIBUTE_COMPRESSED) {
 376             FAttributes.push_back(faCompressed);
 377         }
 378         if (attributes | FILE_ATTRIBUTE_DEVICE) {
 379             FAttributes.push_back(faDevice);
 380         }
 381         if (attributes | FILE_ATTRIBUTE_DIRECTORY) {
 382             FAttributes.push_back(faDirectory);
 383         }
 384         if (attributes | FILE_ATTRIBUTE_ENCRYPTED) {
 385             FAttributes.push_back(faEncrypted);
 386         }
 387         if (attributes | FILE_ATTRIBUTE_HIDDEN) {
 388             FAttributes.push_back(faHidden);
 389         }
 390         if (attributes | FILE_ATTRIBUTE_NORMAL) {
 391             FAttributes.push_back(faNormal);
 392         }
 393         if (attributes | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) {
 394             FAttributes.push_back(faNotContentIndexed);
 395         }
 396         if (attributes | FILE_ATTRIBUTE_SYSTEM) {
 397             FAttributes.push_back(faSystem);
 398         }
 399         if (attributes | FILE_ATTRIBUTE_OFFLINE) {
 400             FAttributes.push_back(faOffline);
 401         }
 402         if (attributes | FILE_ATTRIBUTE_REPARSE_POINT) {
 403             FAttributes.push_back(faSymbolicLink);
 404         }
 405         if (attributes | FILE_ATTRIBUTE_SPARSE_FILE) {
 406             FAttributes.push_back(faSparceFile);
 407         }
 408         if (attributes | FILE_ATTRIBUTE_READONLY ) {
 409             FAttributes.push_back(faReadOnly);
 410         }
 411         if (attributes | FILE_ATTRIBUTE_TEMPORARY) {
 412             FAttributes.push_back(faTemporary);
 413         }
 414         if (attributes | FILE_ATTRIBUTE_VIRTUAL) {
 415             FAttributes.push_back(faVirtual);
 416         }
 417     }
 418 
 419     return result;
 420 }
 421 
 422 bool FileAttributes::Valid(const FileAttribute Value) {
 423     bool result = false;
 424 
 425     switch (Value) {
 426         case faHidden:
 427         case faReadOnly: {
 428             result = true;
 429             break;
 430         }
 431         default:
 432             break;
 433     }
 434 
 435     return result;
 436 }
 437 
 438 void FileAttributes::Append(FileAttribute Value) {
 439     if (Valid(Value) == true) {
 440         FAttributes.push_back(Value);
 441         WriteAttributes();
 442     }
 443 }
 444 
 445 bool FileAttributes::Contains(FileAttribute Value) {
 446     bool result = false;
 447 
 448     std::vector<FileAttribute>::const_iterator iterator =
 449             std::find(FAttributes.begin(), FAttributes.end(), Value);
 450 
 451     if (iterator != FAttributes.end()) {
 452         result = true;
 453     }
 454 
 455     return result;
 456 }
 457 
 458 void FileAttributes::Remove(FileAttribute Value) {
 459     if (Valid(Value) == true) {
 460         std::vector<FileAttribute>::iterator iterator =
 461             std::find(FAttributes.begin(), FAttributes.end(), Value);
 462 
 463         if (iterator != FAttributes.end()) {
 464             FAttributes.erase(iterator);
 465             WriteAttributes();
 466         }
 467     }
 468 }