Beispiel #1
0
int
File_MakeSafeTemp(ConstUnicode tag,  // IN (OPT):
                  Unicode *presult)  // OUT:
{
   int fd;
   Unicode dir = NULL;
   Unicode fileName = NULL;

   ASSERT(presult);

   *presult = NULL;

   if (tag && File_IsFullPath(tag)) {
      File_GetPathName(tag, &dir, &fileName);
   } else {
      dir = File_GetSafeTmpDir(TRUE);
      fileName = Unicode_Duplicate(tag ? tag : "vmware");
   }

   fd = File_MakeTempEx(dir, fileName, presult);

   Unicode_Free(dir);
   Unicode_Free(fileName);

   return fd;
}
int
FileUnlockIntrinsic(ConstUnicode pathName,  // IN:
                    const void *lockToken)  // IN:
{
   int err;

   ASSERT(pathName);
   ASSERT(lockToken);

   LOG(1, ("Requesting unlock on %s\n", UTF8(pathName)));

   if (lockToken == &implicitReadToken) {
      /*
       * The lock token is the fixed-address implicit read lock token.
       * Since no lock file was created no further action is required.
       */

      err = 0;
   } else {
      Unicode lockDir;

      /* The lock directory path */
      lockDir = Unicode_Append(pathName, FILELOCK_SUFFIX);

      /*
       * The lock token is the (unicode) path of the lock file.
       *
       * TODO: under vmx86_debug validate the contents of the lock file as
       *       matching the machineID and executionID.
       */

      err = FileDeletionRobust((Unicode) lockToken, FALSE);

      if (err && vmx86_debug) {
         Log(LGPFX" %s failed for '%s': %s\n",
             __FUNCTION__, (char *) lockToken, strerror(err));
      }

      /*
       * The lockToken (a unicode path) was allocated in FileLockIntrinsic
       * and returned to the caller hidden behind a "void *" pointer.
       */

      Unicode_Free((Unicode) lockToken);

      FileRemoveDirectoryRobust(lockDir); // just in case we can clean up

      Unicode_Free(lockDir);
   }

   return err;
}
Beispiel #3
0
void 
File_GetPathName(ConstUnicode fullPath,  // IN:
                 Unicode *pathName,      // OUT (OPT):
                 Unicode *baseName)      // OUT (OPT):
{
   Unicode volume;
   UnicodeIndex len;
   UnicodeIndex curLen;

   File_SplitName(fullPath, &volume, pathName, baseName);

   if (pathName == NULL) {
      Unicode_Free(volume);
      return;
   }

   /*
    * The volume component may be empty.
    */

   if (!Unicode_IsEmpty(volume)) {
      Unicode temp = Unicode_Append(volume, *pathName);

      Unicode_Free(*pathName);
      *pathName = temp;
   }
   Unicode_Free(volume);

   /*
    * Remove any trailing directory separator characters.
    */

   len = Unicode_LengthInCodePoints(*pathName);

   curLen = len;

   while ((curLen > 0) &&
          (FileFirstSlashIndex(*pathName, curLen - 1) == curLen - 1)) {
      curLen--;
   }

   if (curLen < len) {
      Unicode temp = Unicode_Substr(*pathName, 0, curLen);

      Unicode_Free(*pathName);
      *pathName = temp;
   }
}
static int
RemoveLockingFile(ConstUnicode lockDir,   // IN:
                  ConstUnicode fileName)  // IN:
{
   int err;
   Unicode path;

   ASSERT(lockDir);
   ASSERT(fileName);

   path = Unicode_Join(lockDir, DIRSEPS, fileName, NULL);

   err = FileDeletionRobust(path, FALSE);

   if (err != 0) {
      if (err == ENOENT) {
         /* Not there anymore; locker unlocked or timed out */
         err = 0;
      } else {
         Warning(LGPFX" %s of '%s' failed: %s\n", __FUNCTION__,
                 UTF8(path), strerror(err));
      }
   }

   Unicode_Free(path);

   return err;
}
Unicode
Unicode_Join(ConstUnicode first,  // IN:
             ...)                 // IN
{
   va_list args;
   Unicode result;
   ConstUnicode cur;

   if (first == NULL) {
      return NULL;
   }

   result = Unicode_Duplicate(first);

   va_start(args, first);

   while ((cur = va_arg(args, ConstUnicode)) != NULL) {
      Unicode temp;

      temp = Unicode_Append(result, cur);
      Unicode_Free(result);
      result = temp;
   }

   va_end(args);

   return result;
}
static char *
FileLockLocationChecksum(ConstUnicode path)  // IN:
{
   int c;
   uint32 hash = 5381;

#if defined(_WIN32)
   char *p;
   Unicode value = Unicode_Duplicate(path);

   /* Don't get fooled by mixed case; "normalize" */
   Str_ToLower(value);
   p = value;
#else
   char *p = (char *) path;
#endif

   /* DBJ2 hash... good enough? */
   while ((c = *p++)) {
      hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
   }

#if defined(_WIN32)
   Unicode_Free(value);
#endif

   return Str_SafeAsprintf(NULL, "%u", hash);
}
Bool
FileLockIsLocked(ConstUnicode pathName,  // IN:
                 int *err)               // OUT:
{
   uint32 i;
   int errValue;
   int numEntries;
   Unicode lockDir;

   Bool isLocked = FALSE;
   Unicode *fileList = NULL;

   lockDir = Unicode_Append(pathName, FILELOCK_SUFFIX);

   numEntries = FileListDirectoryRobust(lockDir, &fileList);

   if (numEntries == -1) {
      errValue = errno;

      goto bail;
   }

   for (i = 0; i < numEntries; i++) {
      if (Unicode_StartsWith(fileList[i], "M")) {
         isLocked = TRUE;
         break;
      }
   }

   for (i = 0; i < numEntries; i++) {
      Unicode_Free(fileList[i]);
   }

   free(fileList);

   errValue = 0;

bail:
   Unicode_Free(lockDir);

   if (err != NULL) {
      *err = errValue;
   }

   return isLocked;
}
Beispiel #8
0
void
FileIO_Cleanup(FileIODescriptor *fd)  // IN/OUT:
{
    ASSERT(fd);

    if (fd->fileName) {
        Unicode_Free(fd->fileName);
        fd->fileName = NULL;
    }
}
Unicode
System_GetTimeAsString(void)
{
   struct timeval tv;
   time_t sec;
   int msec;
   size_t charsWritten;
   size_t bufSize = 8; // Multiplied by 2 for the initial allocation.
   char *buf = NULL;
   Unicode dateTime = NULL;
   Unicode output = NULL;

   if (gettimeofday(&tv, NULL)) {
      goto out;
   }
   sec = tv.tv_sec;
   msec = tv.tv_usec / 1000;

   /*
    * Loop repeatedly trying to format the time into a buffer, doubling the
    * buffer with each failure. This should be safe as the manpage for
    * strftime(3) seems to suggest that it only fails if the buffer isn't large
    * enough.
    *
    * The resultant string is encoded according to the current locale.
    */
   do {
      char *newBuf;
      bufSize *= 2;

      newBuf = realloc(buf, bufSize);
      if (newBuf == NULL) {
         goto out;
      }
      buf = newBuf;
      charsWritten = strftime(buf, bufSize, "%b %d %H:%M:%S", localtime(&sec));
   } while (charsWritten == 0);

   /*
    * Append the milliseconds field, but only after converting the date/time
    * string from encoding specified in the current locale to an opaque type.
    */
   dateTime = Unicode_Alloc(buf, STRING_ENCODING_DEFAULT);
   if (dateTime == NULL) {
      goto out;
   }
   output = Unicode_Format("%s.%03d", dateTime, msec);

  out:
   free(buf);
   Unicode_Free(dateTime);
   return output;
}
Beispiel #10
0
Bool
File_DoesVolumeSupportAcls(ConstUnicode path)  // IN:
{
   Bool succeeded = FALSE;

#if defined(_WIN32)
   Bool res;
   Unicode vol, vol2;
   const utf16_t *vol2W;
   DWORD fsFlags;

   ASSERT(path);

   File_SplitName(path, &vol, NULL, NULL);
   vol2 = Unicode_Append(vol, DIRSEPS);

   vol2W = UNICODE_GET_UTF16(vol2);
   res = GetVolumeInformationW(vol2W, NULL, 0, NULL, NULL, &fsFlags, NULL, 0);
   UNICODE_RELEASE_UTF16(vol2W);

   if (res) {
      if ((fsFlags & FS_PERSISTENT_ACLS) == 0) {
         goto exit;
      }
   } else {
      Log(LGPFX" %s: GetVolumeInformation failed: %d\n", __FUNCTION__,
          GetLastError());
      goto exit;
   }

   succeeded = TRUE;

  exit:
   Unicode_Free(vol);
   Unicode_Free(vol2);
#endif

   return succeeded;
}
Beispiel #11
0
Unicode
File_PathJoin(ConstUnicode dirName,   // IN:
              ConstUnicode baseName)  // IN: See above.
{
   Unicode result;
   Unicode newDir = NULL;

   ASSERT(dirName);
   ASSERT(baseName);

   /*
    * Remove ALL directory separators from baseName begin.
    */
#if defined(_WIN32)
   {
      ConstUnicode oldBaseName = baseName;

      /*
       * Reject drive letters in baseName.
       */
      ASSERT(Unicode_LengthInCodePoints(baseName) < 2 ||
             Unicode_FindSubstrInRange(baseName, 1, 1, ":", 0, 1) ==
             UNICODE_INDEX_NOT_FOUND);

      while (*baseName == '/' || *baseName == '\\') {
         baseName++;
      }

      /*
       * Reject UNC paths for baseName.
       */
      ASSERT(baseName - oldBaseName < 2);
   }
#else
   while (*baseName == '/') {
      baseName++;
   }
#endif

   /*
    * Remove ALL directory separators from dirName end.
    */
   newDir = File_StripSlashes(dirName);

   result = Unicode_Join(newDir, DIRSEPS, baseName, NULL);
   Unicode_Free(newDir);

   return result;
}
Beispiel #12
0
Unicode
FileIO_AtomicTempPath(ConstUnicode path)  // IN:
{
    Unicode srcPath;
    Unicode retPath;

    srcPath = File_FullPath(path);
    if (!srcPath) {
        Log("%s: File_FullPath of '%s' failed.\n", __FUNCTION__, path);
        return NULL;
    }
    retPath = Unicode_Join(srcPath, "~", NULL);
    Unicode_Free(srcPath);

    return retPath;
}
Beispiel #13
0
Unicode
File_MakeSafeTempDir(ConstUnicode prefix)  // IN:
{
   Unicode result = NULL;
   Unicode dir = File_GetSafeTmpDir(TRUE);

   if (dir != NULL) {
      ConstUnicode effectivePrefix = (prefix == NULL) ? "safeDir" : prefix;

      File_MakeTempEx2(dir, FALSE, FileMakeTempExCreateNameFunc,
                       (void *) effectivePrefix, &result);

      Unicode_Free(dir);
   }

   return result;
}
ConstUnicode
Unicode_GetStatic(const char *asciiBytes, // IN
                  Bool unescape)          // IN
{
   Unicode result = NULL;
   HashTable *stringTable;

   if (unescape) {
      stringTable = HashTable_AllocOnce(&UnicodeUnescapedStringTable, 4096, 
                                        HASH_FLAG_ATOMIC | HASH_STRING_KEY,
                                        UnicodeHashFree);
   } else {
      stringTable = HashTable_AllocOnce(&UnicodeStringTable, 4096, 
                                        HASH_FLAG_ATOMIC | HASH_STRING_KEY,
                                        UnicodeHashFree);
   }

   /*
    * Attempt a lookup for the key value; if it is found things are easy and
    * fine. Otherwise HashTable_LookupOrInsert is used to attempt to enter
    * the data in a racey manner. Should multiple threads attempt to enter
    * the same key concurrently one thread will get the entered data and the
    * other threads will detect that their entries were rejected; they
    * discard their copies of the data and use the entered data (values
    * will be stable).
    */

   if (!HashTable_Lookup(stringTable, asciiBytes, (void **) &result)) {
      Unicode newData = UnicodeAllocStatic(asciiBytes, unescape);

      if (newData) {
         result = HashTable_LookupOrInsert(stringTable, asciiBytes, newData);

         if (result != newData) {
            Unicode_Free(newData);
         }
      }
   }

   return result;
}
Beispiel #15
0
Bool
FileIO_CloseAndUnlink(FileIODescriptor *fd)  // IN:
{
    Unicode path;
    Bool ret;

    ASSERT(fd);
    ASSERT(FileIO_IsValid(fd));

    path = Unicode_Duplicate(fd->fileName);

    ret = FileIO_Close(fd);
    if (!ret) {
        if (File_UnlinkIfExists(path) == -1) {
            ret = TRUE;
        }
    }

    Unicode_Free(path);

    return ret;
}
Beispiel #16
0
void
File_SplitName(ConstUnicode pathName,  // IN:
               Unicode *volume,        // OUT (OPT):
               Unicode *directory,     // OUT (OPT):
               Unicode *base)          // OUT (OPT):
{
   Unicode vol;
   Unicode dir;
   Unicode bas;
   UnicodeIndex volEnd;
   UnicodeIndex length;
   UnicodeIndex baseBegin;
   WIN32_ONLY(UnicodeIndex pathLen);

   ASSERT(pathName);

   /*
    * Get volume.
    */

   volEnd = 0;

#if defined(_WIN32)
   pathLen = Unicode_LengthInCodePoints(pathName);
   if ((pathLen > 2) &&
       (Unicode_StartsWith(pathName, "\\\\") ||
        Unicode_StartsWith(pathName, "//"))) {
      /* UNC path */
      volEnd = FileFirstSlashIndex(pathName, 2);

      if (volEnd == UNICODE_INDEX_NOT_FOUND) {
         /* we have \\foo, which is just bogus */
         volEnd = 0;
      } else {
         volEnd = FileFirstSlashIndex(pathName, volEnd + 1);

         if (volEnd == UNICODE_INDEX_NOT_FOUND) {
            /* we have \\foo\bar, which is legal */
            volEnd = pathLen;
         }
      }
   } else if ((pathLen >= 2) &&
              (Unicode_FindSubstrInRange(pathName, 1, 1, ":", 0,
                                         1) != UNICODE_INDEX_NOT_FOUND)) {
      /* drive-letter path */
      volEnd = 2;
   }

   if (volEnd > 0) {
      vol = Unicode_Substr(pathName, 0, volEnd);
   } else {
      vol = Unicode_Duplicate("");
   }
#else
   vol = Unicode_Duplicate("");
#endif /* _WIN32 */

   /*
    * Get base.
    */

   baseBegin = FileLastSlashIndex(pathName, 0);
   baseBegin = (baseBegin == UNICODE_INDEX_NOT_FOUND) ? 0 : baseBegin + 1;

   if (baseBegin >= volEnd) {
      bas = Unicode_Substr(pathName, baseBegin, -1);
   } else {
      bas = Unicode_Duplicate("");
   }

   /*
    * Get dir.
    */

   length = baseBegin - volEnd;

   if (length > 0) {
      dir = Unicode_Substr(pathName, volEnd, length);
   } else {
      dir = Unicode_Duplicate("");
   }

   /*
    * Return what needs to be returned.
    */

   if (volume) {
      *volume = vol;
   } else {
      Unicode_Free(vol);
   }

   if (directory) {
      *directory = dir;
   } else {
      Unicode_Free(dir);
   }

   if (base) {
      *base = bas;
   } else {
      Unicode_Free(bas);
   }
}
int
Unicode_CompareRange(ConstUnicode str1,       // IN
                     UnicodeIndex str1Start,  // IN
                     UnicodeIndex str1Length, // IN
                     ConstUnicode str2,       // IN
                     UnicodeIndex str2Start,  // IN
                     UnicodeIndex str2Length, // IN
                     Bool ignoreCase)         // IN
{
   int result = -1;
   Unicode substr1 = NULL;
   Unicode substr2 = NULL;
   utf16_t *substr1UTF16 = NULL;
   utf16_t *substr2UTF16 = NULL;
   UnicodeIndex i = 0;
   UnicodeIndex utf16Index;
   utf16_t codeUnit1;
   utf16_t codeUnit2;
   uint32 codePoint1;
   uint32 codePoint2;

   UnicodePinIndices(str1, &str1Start, &str1Length);
   UnicodePinIndices(str2, &str2Start, &str2Length);

   /*
    * TODO: Allocating substrings is a performance hit.  We should do
    * this search in-place.  (However, searching UTF-8 requires tender loving
    * care, and it's just easier to search UTF-16.)
    */
   substr1 = Unicode_Substr(str1, str1Start, str1Length);
   if (!substr1) {
      goto out;
   }

   substr2 = Unicode_Substr(str2, str2Start, str2Length);
   if (!substr2) {
      goto out;
   }

   /*
    * XXX TODO: Need to normalize the incoming strings to NFC or NFD.
    */
   substr1UTF16 = Unicode_GetAllocUTF16(substr1);
   if (!substr1UTF16) {
      goto out;
   }

   substr2UTF16 = Unicode_GetAllocUTF16(substr2);
   if (!substr2UTF16) {
      goto out;
   }

   /*
    * TODO: This is the naive string search algorithm, which is
    * O(n * m).  We can do better with KMP or Boyer-Moore if this
    * proves to be a bottleneck.
    */
   while (TRUE) {
      codeUnit1 = *(substr1UTF16 + i);
      codeUnit2 = *(substr2UTF16 + i);

      /*
       * TODO: Simple case folding doesn't handle the situation where
       * more than one code unit is needed to store the result of the
       * case folding.
       *
       * This means that German "straBe" (where B = sharp S, U+00DF)
       * will not match "STRASSE", even though the two strings are the
       * same.
       */
      if (ignoreCase) {
         codeUnit1 = UnicodeSimpleCaseFold(codeUnit1);
         codeUnit2 = UnicodeSimpleCaseFold(codeUnit2);
      }

      if (codeUnit1 != codeUnit2) {
         break;
      }

      if (codeUnit1 == 0) {
         // End of both strings reached: strings are equal.
         result = 0;
         goto out;
      }

      i++;
   }

   /*
    * The two UTF-16 code units differ.  If they're the first code unit
    * of a surrogate pair (for Unicode values past U+FFFF), decode the
    * surrogate pair into a full Unicode code point.
    */
   if (U16_IS_SURROGATE(codeUnit1)) {
      ssize_t substrUTF16Len = Unicode_UTF16Strlen(substr1UTF16);

      // U16_NEXT modifies the index, so let it work on a copy.
      utf16Index = i;

      // Decode the surrogate if needed.
      U16_NEXT(substr1UTF16, utf16Index, substrUTF16Len, codePoint1);
   } else {
      // Not a surrogate?  Then the code point value is the code unit.
      codePoint1 = codeUnit1;
   }

   if (U16_IS_SURROGATE(codeUnit2)) {
      ssize_t substrUTF16Len = Unicode_UTF16Strlen(substr2UTF16);

      utf16Index = i;
      U16_NEXT(substr2UTF16, utf16Index, substrUTF16Len, codePoint2);
   } else {
      codePoint2 = codeUnit2;
   }

   if (codePoint1 < codePoint2) {
      result = -1;
   } else if (codePoint1 > codePoint2) {
      result = 1;
   } else {
      // If we hit the end of the string, we've already gone to 'out'.
      NOT_REACHED();
   }

  out:
   free(substr1UTF16);
   free(substr2UTF16);

   Unicode_Free(substr1);
   Unicode_Free(substr2);

   return result;
}
Beispiel #18
0
int
File_MakeTempEx2(ConstUnicode dir,                             // IN:
                 Bool createTempFile,                          // IN:
                 File_MakeTempCreateNameFunc *createNameFunc,  // IN:
                 void *createNameFuncData,                     // IN:
                 Unicode *presult)                             // OUT:
{
   uint32 i;

   int fd = -1;
   uint32 var = 0;
   Unicode path = NULL;

   if ((dir == NULL) || (createNameFunc == NULL)) {
      errno = EFAULT;

      return -1;
   }

   ASSERT(presult);

   *presult = NULL;

   for (i = 0; i < (MAX_INT32 / 2); i++) {
      Unicode fileName;

      /* construct suffixed pathname to use */
      Unicode_Free(path);
      path = NULL;

      /*
       * Files and directories are kept separate (odd and even respectfully).
       * This way the available exclusion mechanisms work properly - O_EXCL
       * on files, mkdir on directories - and races are avoided.
       *
       * Not attempting an open on a directory is a good thing...
       */

      FileTempNum(createTempFile, &var);

      fileName = (*createNameFunc)(var, createNameFuncData);
      ASSERT(fileName);

      /* construct base full pathname to use */
      path = Unicode_Join(dir, DIRSEPS, fileName, NULL);

      Unicode_Free(fileName);

      if (createTempFile) {
         fd = Posix_Open(path, O_CREAT | O_EXCL | O_BINARY | O_RDWR, 0600);
      } else {
         fd = Posix_Mkdir(path, 0700);
      }

      if (fd != -1) {
         *presult = path;
         path = NULL;
         break;
      }

      if (errno != EEXIST) {
         Log(LGPFX" Failed to create temporary %s \"%s\": %s.\n",
             createTempFile ? "file" : "directory",
             UTF8(path), strerror(errno));
         goto exit;
      }
   }

   if (fd == -1) {
      Warning(LGPFX" Failed to create temporary %s \"%s\": "
              "The name space is full.\n",
              createTempFile ? "file" : "directory", UTF8(path));

      errno = EAGAIN;
   }

  exit:
   Unicode_Free(path);

   return fd;
}
Beispiel #19
0
Bool
FileIO_AtomicUpdate(FileIODescriptor *newFD,   // IN/OUT: file IO descriptor
                    FileIODescriptor *currFD)  // IN/OUT: file IO descriptor
{
    char *currPath = NULL;
    char *newPath = NULL;
#if defined(_WIN32)
    uint32 currAccess;
    uint32 newAccess;
    FileIOResult status;
    FileIODescriptor tmpFD;
#else
    int fd;
#endif
    int savedErrno = 0;
    Bool ret = FALSE;

    ASSERT(FileIO_IsValid(newFD));
    ASSERT(FileIO_IsValid(currFD));

    if (HostType_OSIsVMK()) {
#if defined(VMX86_SERVER)
        FS_SwapFilesArgs *args = NULL;
        char *dirName = NULL;
        char *fileName = NULL;
        char *dstDirName = NULL;
        char *dstFileName = NULL;

        currPath = File_FullPath(FileIO_Filename(currFD));
        if (!currPath) {
            savedErrno = errno;
            Log("%s: File_FullPath of '%s' failed.\n", __FUNCTION__,
                FileIO_Filename(currFD));
            goto swapdone;
        }

        newPath = File_FullPath(FileIO_Filename(newFD));
        if (!newPath) {
            savedErrno = errno;
            Log("%s: File_FullPath of '%s' failed.\n", __FUNCTION__,
                FileIO_Filename(newFD));
            goto swapdone;
        }

        File_GetPathName(newPath, &dirName, &fileName);
        File_GetPathName(currPath, &dstDirName, &dstFileName);

        ASSERT(dirName && *dirName);
        ASSERT(fileName && *fileName);
        ASSERT(dstDirName && *dstDirName);
        ASSERT(dstFileName && *dstFileName);
        ASSERT(!strcmp(dirName, dstDirName));

        args = (FS_SwapFilesArgs *) Util_SafeCalloc(1, sizeof(*args));
        if (Str_Snprintf(args->srcFile, sizeof(args->srcFile), "%s",
                         fileName) < 0) {
            Log("%s: Path too long \"%s\".\n", __FUNCTION__, fileName);
            savedErrno = ENAMETOOLONG;
            goto swapdone;
        }
        if (Str_Snprintf(args->dstFilePath, sizeof(args->dstFilePath), "%s/%s",
                         dstDirName, dstFileName) < 0) {
            Log("%s: Path too long \"%s\".\n", __FUNCTION__, dstFileName);
            savedErrno = ENAMETOOLONG;
            goto swapdone;
        }

        /*
         * Issue the ioctl on the directory rather than on the file,
         * because the file could be open.
         */

        fd = Posix_Open(dirName, O_RDONLY);
        if (fd < 0) {
            Log("%s: Open failed \"%s\" %d.\n", __FUNCTION__, dirName, errno);
            ASSERT(errno != EBUSY);   /* #615124. */
            savedErrno = errno;
            goto swapdone;
        }

        if (ioctl(fd, IOCTLCMD_VMFS_SWAP_FILES, args) != 0) {
            savedErrno = errno;
            if (errno != ENOSYS && errno != ENOTTY) {
                Log("%s: ioctl failed %d.\n", __FUNCTION__, errno);
                ASSERT(errno != EBUSY);   /* #615124. */
            }
        } else {
            ret = TRUE;
        }

        close(fd);

        /*
         * Did we fail because we are on a file system that does not
         * support the IOCTLCMD_VMFS_SWAP_FILES ioctl? If so fallback to
         * using rename.
         *
         * Check for both ENOSYS and ENOTTY. PR 957695
         */
        if (savedErrno == ENOSYS || savedErrno == ENOTTY) {
            /*
             * NFS allows renames of locked files, even if both files
             * are locked.  The file lock follows the file handle, not
             * the name, so after the rename we can swap the underlying
             * file descriptors instead of closing and reopening the
             * target file.
             *
             * This is different than the hosted path below because
             * ESX uses native file locks and hosted does not.
             *
             * We assume that all ESX file systems that support rename
             * have the same file lock semantics as NFS.
             */

            if (File_Rename(newPath, currPath)) {
                Log("%s: rename of '%s' to '%s' failed %d.\n",
                    __FUNCTION__, newPath, currPath, errno);
                savedErrno = errno;
                goto swapdone;
            }
            ret = TRUE;
            fd = newFD->posix;
            newFD->posix = currFD->posix;
            currFD->posix = fd;
            FileIO_Close(newFD);
        }

swapdone:
        free(args);
        free(dirName);
        free(fileName);
        free(dstDirName);
        free(dstFileName);
        free(currPath);
        free(newPath);

        errno = savedErrno;
        return ret;
#else
        NOT_REACHED();
#endif
    }
#if defined(_WIN32)
    currPath = Unicode_Duplicate(FileIO_Filename(currFD));
    newPath = Unicode_Duplicate(FileIO_Filename(newFD));

    newAccess = newFD->flags;
    currAccess = currFD->flags;

    FileIO_Close(newFD);

    /*
     * The current file needs to be closed and reopened,
     * but we don't want to drop the file lock by calling
     * FileIO_Close() on it.  Instead, use native close primitives.
     * We'll reopen it later with FileIO_Open.  Set the
     * descriptor/handle to an invalid value while we're in the
     * middle of transferring ownership.
     */

    CloseHandle(currFD->win32);
    currFD->win32 = INVALID_HANDLE_VALUE;
    if (File_RenameRetry(newPath, currPath, 10) == 0) {
        ret = TRUE;
    } else {
        savedErrno = errno;
        ASSERT(!ret);
    }

    FileIO_Invalidate(&tmpFD);

    /*
     * Clear the locking bits from the requested access so that reopening
     * the file ignores the advisory lock.
     */

    ASSERT((currAccess & FILEIO_OPEN_LOCK_MANDATORY) == 0);
    currAccess &= ~(FILEIO_OPEN_LOCK_MANDATORY | FILEIO_OPEN_LOCK_ADVISORY |
                    FILEIO_OPEN_LOCK_BEST | FILEIO_OPEN_LOCKED);
    status = FileIO_Open(&tmpFD, currPath, currAccess, FILEIO_OPEN);
    if (!FileIO_IsSuccess(status)) {
        Panic("Failed to reopen dictionary after renaming "
              "\"%s\" to \"%s\": %s (%d)\n", newPath, currPath,
              FileIO_ErrorEnglish(status), status);
    }
    ASSERT(tmpFD.lockToken == NULL);

    currFD->win32 = tmpFD.win32;

    FileIO_Cleanup(&tmpFD);
    Unicode_Free(currPath);
    Unicode_Free(newPath);
    errno = savedErrno;

    return ret;
#else
    currPath = (char *)FileIO_Filename(currFD);
    newPath = (char *)FileIO_Filename(newFD);

    if (File_Rename(newPath, currPath)) {
        Log("%s: rename of '%s' to '%s' failed %d.\n",
            __FUNCTION__, newPath, currPath, errno);
        savedErrno = errno;
    } else {
        ret = TRUE;
        fd = newFD->posix;
        newFD->posix = currFD->posix;
        currFD->posix = fd;
        FileIO_Close(newFD);
    }

    errno = savedErrno;

    return ret;
#endif
}
Beispiel #20
0
FileIOResult
FileIO_AtomicTempFile(FileIODescriptor *fileFD,  // IN:
                      FileIODescriptor *tempFD)  // OUT:
{
    Unicode tempPath = NULL;
    int permissions;
    FileIOResult status;
#if !defined(_WIN32)
    int ret;
    struct stat stbuf;
#endif

    ASSERT(FileIO_IsValid(fileFD));
    ASSERT(tempFD && !FileIO_IsValid(tempFD));

    tempPath = FileIO_AtomicTempPath(FileIO_Filename(fileFD));
    if (!tempPath) {
        status = FILEIO_ERROR;
        goto bail;
    }

#if defined(_WIN32)
    permissions = 0;
    File_UnlinkIfExists(tempPath);
#else
    if (fstat(fileFD->posix, &stbuf)) {
        Log("%s: Failed to fstat '%s', errno: %d.\n", __FUNCTION__,
            FileIO_Filename(fileFD), errno);
        status = FILEIO_ERROR;
        goto bail;
    }
    permissions = stbuf.st_mode;

    /* Clean up a previously created temp file; if one exists. */
    ret = Posix_Unlink(tempPath);
    if (ret != 0 && errno != ENOENT) {
        Log("%s: Failed to unlink temporary file, errno: %d\n",
            __FUNCTION__, errno);
        /* Fall through; FileIO_Create will report the actual error. */
    }
#endif

    status = FileIO_Create(tempFD, tempPath,
                           FILEIO_ACCESS_READ | FILEIO_ACCESS_WRITE,
                           FILEIO_OPEN_CREATE_SAFE, permissions);
    if (!FileIO_IsSuccess(status)) {
        Log("%s: Failed to create temporary file, %s (%d). errno: %d\n",
            __FUNCTION__, FileIO_ErrorEnglish(status), status, Err_Errno());
        goto bail;
    }

#if !defined(_WIN32)
    /*
     * On ESX we always use the vmkernel atomic file swap primitive, so
     * there's no need to set the permissions and owner of the temp file.
     *
     * XXX this comment is not true for NFS on ESX -- we use rename rather
     * than "vmkernel atomic file swap primitive" -- but we do not care
     * because files are always owned by root.  Sigh.  Bug 839283.
     */

    if (!HostType_OSIsVMK()) {
        if (fchmod(tempFD->posix, stbuf.st_mode)) {
            Log("%s: Failed to chmod temporary file, errno: %d\n",
                __FUNCTION__, errno);
            status = FILEIO_ERROR;
            goto bail;
        }
        if (fchown(tempFD->posix, stbuf.st_uid, stbuf.st_gid)) {
            Log("%s: Failed to chown temporary file, errno: %d\n",
                __FUNCTION__, errno);
            status = FILEIO_ERROR;
            goto bail;
        }
    }
#endif

    Unicode_Free(tempPath);
    return FILEIO_SUCCESS;

bail:
    ASSERT(!FileIO_IsSuccess(status));
    if (FileIO_IsValid(tempFD)) {
        FileIO_Close(tempFD);
#if defined(_WIN32)
        File_UnlinkIfExists(tempPath);
#else
        ret = Posix_Unlink(tempPath);
        if (ret != 0) {
            Log("%s: Failed to clean up temporary file, errno: %d\n",
                __FUNCTION__, errno);
        }
        ASSERT(ret == 0);
#endif
    }
    Unicode_Free(tempPath);
    return status;
}
static int
WaitForPossession(ConstUnicode lockDir,      // IN:
                  ConstUnicode fileName,     // IN:
                  LockValues *memberValues,  // IN:
                  LockValues *myValues)      // IN:
{
   int err = 0;

   ASSERT(lockDir);
   ASSERT(fileName);

   /* "Win" or wait? */
   if (((memberValues->lamportNumber < myValues->lamportNumber) ||
       ((memberValues->lamportNumber == myValues->lamportNumber) &&
          (Unicode_Compare(memberValues->memberName,
                           myValues->memberName) < 0))) &&
        ((strcmp(memberValues->lockType, LOCK_EXCLUSIVE) == 0) ||
         (strcmp(myValues->lockType, LOCK_EXCLUSIVE) == 0))) {
      Unicode path;
      uint32 loopCount;
      Bool   thisMachine; 

      thisMachine = FileLockMachineIDMatch(myValues->machineID,
                                           memberValues->machineID);

      loopCount = 0;

      path = Unicode_Join(lockDir, DIRSEPS, fileName, NULL);

      while ((err = Sleeper(myValues, &loopCount)) == 0) {
         /* still there? */
         err = FileAttributesRobust(path, NULL);
         if (err != 0) {
            if (err == ENOENT) {
               /* Not there anymore; locker unlocked or timed out */
               err = 0;
            }

            break;
         }

         /* still valid? */
         if (thisMachine && !FileLockValidOwner(memberValues->executionID,
                                                memberValues->payload)) {
            /* Invalid Execution ID; remove the member file */
            Warning(LGPFX" %s discarding file '%s'; invalid executionID.\n",
                    __FUNCTION__, UTF8(path));

            err = RemoveLockingFile(lockDir, fileName);
            break;
         }
      }

      /*
       * Log the disposition of each timeout for all non "try lock" locking
       * attempts. This can assist in debugging locking problems.
       */

      if ((myValues->msecMaxWaitTime != FILELOCK_TRYLOCK_WAIT) &&
          (err == EAGAIN)) {
         if (thisMachine) {
            Log(LGPFX" %s timeout on '%s' due to a local process (%s)\n",
                    __FUNCTION__, UTF8(path), memberValues->executionID);
         } else {
            Log(LGPFX" %s timeout on '%s' due to another machine (%s)\n",
                    __FUNCTION__, UTF8(path), memberValues->machineID);
         }
      }

      Unicode_Free(path);
   }

   return err;
}
static int
CreateEntryDirectory(const char *machineID,    // IN:
                     const char *executionID,  // IN:
                     ConstUnicode lockDir,     // IN:
                     Unicode *entryDirectory,  // OUT:
                     Unicode *entryFilePath,   // OUT:
                     Unicode *memberFilePath,  // OUT:
                     Unicode *memberName)      // OUT:
{
   int err = 0;
   uint32 randomNumber = 0;

   ASSERT(lockDir);

   *entryDirectory = NULL;
   *entryFilePath = NULL;
   *memberFilePath = NULL;
   *memberName = NULL;

   /* Fun at the races */

   while (TRUE) {
      Unicode temp;
      FileData fileData;

      err = FileAttributesRobust(lockDir, &fileData);
      if (err == 0) {
        /* The name exists. Deal with it... */

        if (fileData.fileType == FILE_TYPE_REGULAR) {
           /*
            * It's a file. Assume this is an (active?) old style lock and
            * err on the safe side - don't remove it (and automatically
            * upgrade to a new style lock).
            */

            Log(LGPFX" %s: '%s' exists; an old style lock file?\n",
                      __FUNCTION__, UTF8(lockDir));

            err = EAGAIN;
            break;
        }

        if (fileData.fileType != FILE_TYPE_DIRECTORY) {
           /* Not a directory; attempt to remove the debris */
           if (FileDeletionRobust(lockDir, FALSE) != 0) {
              Warning(LGPFX" %s: '%s' exists and is not a directory.\n",
                      __FUNCTION__, UTF8(lockDir));

              err = ENOTDIR;
              break;
           }

           continue;
        }
      } else {
         if (err == ENOENT) {
            /* Not there anymore; locker unlocked or timed out */
            err = MakeDirectory(lockDir);

            if ((err != 0) && (err != EEXIST)) {
               Warning(LGPFX" %s creation failure on '%s': %s\n",
                       __FUNCTION__, UTF8(lockDir), strerror(err));

               break;
            }
         } else {
            Warning(LGPFX" %s stat failure on '%s': %s\n",
                    __FUNCTION__, UTF8(lockDir), strerror(err));

            break;
         }
      }

      /* There is a small chance of collision/failure; grab stings now */
      randomNumber = SimpleRandomNumber(machineID, executionID);

      *memberName = Unicode_Format("M%05u%s", randomNumber, FILELOCK_SUFFIX);

      temp = Unicode_Format("D%05u%s", randomNumber, FILELOCK_SUFFIX);
      *entryDirectory = Unicode_Join(lockDir, DIRSEPS, temp, NULL);
      Unicode_Free(temp);

      temp = Unicode_Format("E%05u%s", randomNumber, FILELOCK_SUFFIX);
      *entryFilePath = Unicode_Join(lockDir, DIRSEPS, temp, NULL);
      Unicode_Free(temp);

      *memberFilePath = Unicode_Join(lockDir, DIRSEPS, *memberName, NULL);

      err = MakeDirectory(*entryDirectory);

      if (err == 0) {
         /*
          * The entry directory was safely created. See if a member file
          * is in use (the entry directory is removed once the member file
          * is created). If a member file is in use, choose another number,
          * otherwise the use of the this number is OK.
          *
          * Err on the side of caution... don't want to trash perfectly
          * good member files.
          */

         err = FileAttributesRobust(*memberFilePath, NULL);

         if (err != 0) {
            if (err == ENOENT) {
               err = 0;
               break;
            }

            if (vmx86_debug) {
               Log(LGPFX" %s stat failure on '%s': %s\n",
                   __FUNCTION__, UTF8(*memberFilePath), strerror(err));
             }
         }

         FileRemoveDirectoryRobust(*entryDirectory);
      } else {
          if ((err != EEXIST) &&  // Another process/thread created it...
              (err != ENOENT)) {  // lockDir is gone...
             Warning(LGPFX" %s creation failure on '%s': %s\n",
                     __FUNCTION__, UTF8(*entryDirectory), strerror(err));

             break;
          }
      }

      Unicode_Free(*entryDirectory);
      Unicode_Free(*entryFilePath);
      Unicode_Free(*memberFilePath);
      Unicode_Free(*memberName);

      *entryDirectory = NULL;
      *entryFilePath = NULL;
      *memberFilePath = NULL;
      *memberName = NULL;
   }

   if (err != 0) {
      Unicode_Free(*entryDirectory);
      Unicode_Free(*entryFilePath);
      Unicode_Free(*memberFilePath);
      Unicode_Free(*memberName);

      *entryDirectory = NULL;
      *entryFilePath = NULL;
      *memberFilePath = NULL;
      *memberName = NULL;
   }

   return err;
}
Beispiel #23
0
Unicode
File_ReplaceExtension(ConstUnicode pathName,      // IN:
                      ConstUnicode newExtension,  // IN:
                      uint32 numExtensions,       // IN:
                      ...)                        // IN:
{
   Unicode path;
   Unicode base;
   Unicode result;
   va_list arguments;
   UnicodeIndex index;
   
   ASSERT(pathName);
   ASSERT(newExtension);
   ASSERT(Unicode_StartsWith(newExtension, "."));

   File_GetPathName(pathName, &path, &base);

   index = Unicode_FindLast(base, ".");

   if (index != UNICODE_INDEX_NOT_FOUND) {
      Unicode oldBase = base;

      if (numExtensions) {
         uint32 i;

         /*
          * Only truncate the old extension from the base if it exists in
          * in the valid extensions list.
          */

         va_start(arguments, numExtensions);

         for (i = 0; i < numExtensions ; i++) {
            Unicode oldExtension = va_arg(arguments, Unicode);

            ASSERT(Unicode_StartsWith(oldExtension, "."));

            if (Unicode_CompareRange(base, index, -1,
                                     oldExtension, 0, -1, FALSE) == 0) {
               base = Unicode_Truncate(oldBase, index); // remove '.'
               break;
            }
         }

         va_end(arguments);
      } else {
         /* Always truncate the old extension if extension list is empty . */
         base = Unicode_Truncate(oldBase, index); // remove '.'
      }

      if (oldBase != base) {
         Unicode_Free(oldBase);
      }
   }

   if (Unicode_IsEmpty(path)) {
      result = Unicode_Append(base, newExtension);
   } else {
      result = Unicode_Join(path, DIRSEPS, base, newExtension, NULL);
   }

   Unicode_Free(path);
   Unicode_Free(base);

   return result;
}
void *
FileLockIntrinsic(ConstUnicode pathName,   // IN:
                  Bool exclusivity,        // IN:
                  uint32 msecMaxWaitTime,  // IN:
                  const char *payload,     // IN:
                  int *err)                // OUT:
{
   FILELOCK_FILE_HANDLE handle;
   LockValues myValues;

   Unicode lockDir = NULL;
   Unicode entryFilePath = NULL;
   Unicode memberFilePath = NULL;
   Unicode entryDirectory = NULL;

   ASSERT(pathName);
   ASSERT(err);

   /* Construct the locking directory path */
   lockDir = Unicode_Append(pathName, FILELOCK_SUFFIX);

   /* establish our values */

   myValues.machineID = (char *) FileLockGetMachineID(); // don't free this!
   myValues.executionID = FileLockGetExecutionID();      // free this!
   myValues.payload = (char *) payload;
   myValues.lockType = exclusivity ? LOCK_EXCLUSIVE : LOCK_SHARED;
   myValues.lamportNumber = 0;
   myValues.locationChecksum = FileLockLocationChecksum(lockDir); // free this!
   myValues.waitTime = 0;
   myValues.msecMaxWaitTime = msecMaxWaitTime;
   myValues.memberName = NULL;

   LOG(1, ("Requesting %s lock on %s (%s, %s, %u).\n",
       myValues.lockType, UTF8(pathName), myValues.machineID,
       myValues.executionID, myValues.msecMaxWaitTime));

   /*
    * Attempt to create the locking and entry directories; obtain the
    * entry and member path names.
    */

   *err = CreateEntryDirectory(myValues.machineID, myValues.executionID,
                               lockDir,
                               &entryDirectory, &entryFilePath,
                               &memberFilePath, &myValues.memberName);

   switch (*err) {
   case 0:
      break;

   case EROFS:
      /* FALL THROUGH */
   case EACCES:
      if (!exclusivity) {
         /*
          * Lock is for read/shared access however the lock directory could
          * not be created. Grant an implicit read lock whenever possible.
          * The address of a private variable will be used for the lock token.
          */

         Warning(LGPFX" %s implicit %s lock succeeded on '%s'.\n",
                 __FUNCTION__, LOCK_SHARED, UTF8(pathName));

         *err = 0;
         memberFilePath = &implicitReadToken;
      }

      /* FALL THROUGH */
   default:
      goto bail;
   }

   ASSERT(Unicode_LengthInCodeUnits(memberFilePath) -
          Unicode_LengthInCodeUnits(pathName) <= FILELOCK_OVERHEAD);

   /* Attempt to create the entry file */
   *err = FileLockOpenFile(entryFilePath, O_CREAT | O_WRONLY, &handle);

   if (*err != 0) {
      /* clean up */
      FileRemoveDirectoryRobust(entryDirectory);
      FileRemoveDirectoryRobust(lockDir);

      goto bail;
   }

   /* what is max(Number[1]... Number[all lockers])? */
   *err = Scanner(lockDir, NumberScan, &myValues, FALSE);

   if (*err != 0) {
      /* clean up */
      FileLockCloseFile(handle);
      FileDeletionRobust(entryFilePath, FALSE);
      FileRemoveDirectoryRobust(entryDirectory);
      FileRemoveDirectoryRobust(lockDir);

      goto bail;
   }

   /* Number[i] = 1 + max([Number[1]... Number[all lockers]) */
   myValues.lamportNumber++;

   /* Attempt to create the member file */
   *err = CreateMemberFile(handle, &myValues, entryFilePath, memberFilePath);

   /* Remove entry directory; it has done its job */
   FileRemoveDirectoryRobust(entryDirectory);

   if (*err != 0) {
      /* clean up */
      FileDeletionRobust(entryFilePath, FALSE);
      FileDeletionRobust(memberFilePath, FALSE);
      FileRemoveDirectoryRobust(lockDir);

      goto bail;
   }

   /* Attempt to acquire the lock */
   *err = Scanner(lockDir, WaitForPossession, &myValues, TRUE);

   switch (*err) {
   case 0:
      break;

   case EAGAIN:
      /* clean up */
      FileDeletionRobust(memberFilePath, FALSE);
      FileRemoveDirectoryRobust(lockDir);

      /* FALL THROUGH */
   default:
      break;
   }

bail:

   Unicode_Free(lockDir);
   Unicode_Free(entryDirectory);
   Unicode_Free(entryFilePath);
   Unicode_Free(myValues.memberName);
   free(myValues.locationChecksum);
   free(myValues.executionID);

   if (*err != 0) {
      Unicode_Free(memberFilePath);
      memberFilePath = NULL;

      if (*err == EAGAIN) {
         *err = 0; // lock not acquired
      }
   }

   return (void *) memberFilePath;
}
int
FileLockMemberValues(ConstUnicode lockDir,      // IN:
                     ConstUnicode fileName,     // IN:
                     char *buffer,              // OUT:
                     uint32 requiredSize,       // IN:
                     LockValues *memberValues)  // OUT:
{
#ifndef __MINGW32__
   uint32 argc = 0;
   FILELOCK_FILE_HANDLE handle;
   uint32 len;
   char *argv[FL_MAX_ARGS];
   char *saveptr = NULL;
   int err;
   Unicode path;
   FileData fileData;

   ParseTable table = { PARSE_TABLE_STRING,
                        "lc",
                        (void *) &memberValues->locationChecksum
                      };
 
   ASSERT(lockDir);
   ASSERT(fileName);

   path = Unicode_Join(lockDir, DIRSEPS, fileName, NULL);

   err = FileLockOpenFile(path, O_RDONLY, &handle);

   if (err != 0) {
      /*
       * A member file may "disappear" if is deleted due to an unlock
       * immediately after a directory scan but before the scan is processed.
       * Since this is a "normal" thing ENOENT will be suppressed.
       */

      if (err != ENOENT) {
         Warning(LGPFX" %s open failure on '%s': %s\n", __FUNCTION__,
                 UTF8(path), strerror(err));
      }

      goto bail;
   }

   /* Attempt to obtain the lock file attributes now that it is opened */
   err = FileAttributesRobust(path, &fileData);

   if (err != 0) {
      Warning(LGPFX" %s file size failure on '%s': %s\n", __FUNCTION__,
              UTF8(path), strerror(err));

      FileLockCloseFile(handle);

      goto bail;
   }

   /* Complain if the lock file is not the proper size */
   if (fileData.fileSize != requiredSize) {
      Warning(LGPFX" %s file '%s': size %"FMT64"u, required size %u\n",
              __FUNCTION__, UTF8(path), fileData.fileSize, requiredSize);

      FileLockCloseFile(handle);

      goto corrupt;
   }

   /* Attempt to read the lock file data and validate how much was read. */
   err = FileLockReadFile(handle, buffer, requiredSize, &len);

   FileLockCloseFile(handle);

   if (err != 0) {
      Warning(LGPFX" %s read failure on '%s': %s\n",
              __FUNCTION__, UTF8(path), strerror(err));

      goto bail;
   }

   if (len != requiredSize) {
      Warning(LGPFX" %s read length issue on '%s': %u and %u\n",
              __FUNCTION__, UTF8(path), len, requiredSize);

      err = EIO;
      goto bail;
   }

   /* Extract and validate the lock file data. */
   for (argc = 0; argc < FL_MAX_ARGS; argc++) {
      argv[argc] = strtok_r((argc == 0) ? buffer : NULL, " ", &saveptr);

      if (argv[argc] == NULL) {
         break;
      }
   }

   if ((argc < 4) || ((argc == FL_MAX_ARGS) &&
                       (strtok_r(NULL, " ", &saveptr) != NULL))) {
      goto corrupt;
   }

   /*
    * Lock file arguments are space separated. There is a minimum of 4
    * arguments - machineID, executionID, Lamport number and lock type.
    * The maximum number of arguments is FL_MAX_ARGS.
    *
    * The fifth argument, if present, is the payload or "[" if there is no
    * payload and additional arguments are present. The additional arguments
    * form  a properly list - one or more "name=value" pairs.
    *
    * Here is picture of valid forms:
    *
    * 0 1 2 3 4 5 6   Comment
    *-------------------------
    * A B C D         contents, no payload, no list entries
    * A B C D [       contents, no payload, no list entries
    * A B C D P       contents, a payload,  no list entries
    * A B C D [ x     contents, no payload, one list entry
    * A B C D P x     contents, a payload,  one list entry
    * A B C D [ x y   contents, no payload, two list entries
    * A B C D P x y   contents, a payload,  two list entries
    */

   memberValues->locationChecksum = NULL;

   if (argc == 4) {
      memberValues->payload = NULL;
   } else {
      if (strcmp(argv[4], "[") == 0) {
         memberValues->payload = NULL;
      } else {
         memberValues->payload = argv[4];
      }

      if (FileLockParseArgs(argv, argc - 5, &table, 1)) {
         goto corrupt;
      }
   }

   if (sscanf(argv[2], "%u", &memberValues->lamportNumber) != 1) {
      goto corrupt;
   }

   if ((strcmp(argv[3], LOCK_SHARED) != 0) &&
       (strcmp(argv[3], LOCK_EXCLUSIVE) != 0)) {
      goto corrupt;
   }

   memberValues->machineID = argv[0];
   memberValues->executionID = argv[1];
   memberValues->lockType = argv[3];
   memberValues->memberName = Unicode_Duplicate(fileName);

   Unicode_Free(path);

   return 0;

corrupt:
   Warning(LGPFX" %s removing problematic lock file '%s'\n", __FUNCTION__,
           UTF8(path));

   if (argc) {
      Log(LGPFX" %s '%s' contents are:\n", __FUNCTION__, UTF8(fileName));

      for (len = 0; len < argc; len++) {
         Log(LGPFX" %s %s argv[%d]: '%s'\n", __FUNCTION__, UTF8(fileName),
             len, argv[len]);
      }
   }

   /* Remove the lock file and behave like it has disappeared */
   err = FileDeletionRobust(path, FALSE);

   if (err == 0) {
      err = ENOENT;
   }

bail:
   Unicode_Free(path);

   return err;
#else
   // XXX - incomplete: needs a win/mingw32 implementation, if required.
   NOT_IMPLEMENTED();
   return 0;
#endif
}
int
FileLockHackVMX(ConstUnicode pathName)  // IN:
{
   int err;
   LockValues myValues;

   Unicode lockDir = NULL;
   Unicode entryFilePath = NULL;
   Unicode memberFilePath = NULL;
   Unicode entryDirectory = NULL;

   ASSERT(pathName);

   /* first the locking directory path name */
   lockDir = Unicode_Append(pathName, FILELOCK_SUFFIX);

   /* establish our values */
   myValues.machineID = (char *) FileLockGetMachineID(); // don't free this!
   myValues.executionID = FileLockGetExecutionID();      // free this!
   myValues.locationChecksum = FileLockLocationChecksum(lockDir); // free this!
   myValues.lamportNumber = 0;
   myValues.memberName = NULL;

   LOG(1, ("%s on %s (%s, %s).\n", __FUNCTION__, UTF8(pathName),
       myValues.machineID, myValues.executionID));

   err = CreateEntryDirectory(myValues.machineID, myValues.executionID,
                              lockDir,
                              &entryDirectory, &entryFilePath,
                              &memberFilePath, &myValues.memberName);

   if (err != 0) {
      goto bail;
   }

   /* Scan the lock directory */
   err = Scanner(lockDir, ScannerVMX, &myValues, FALSE);

   if (err == 0) {
      /* if no members are valid, clean up */
      if (myValues.lamportNumber == 1) {
         FileDeletionRobust(pathName, FALSE);
      }
   } else {
      if (vmx86_debug) {
         Warning(LGPFX" %s clean-up failure for '%s': %s\n",
                 __FUNCTION__, UTF8(pathName), strerror(err));
      }
   }

   /* clean up */
   FileRemoveDirectoryRobust(entryDirectory);
   FileRemoveDirectoryRobust(lockDir);

bail:

   Unicode_Free(lockDir);
   Unicode_Free(entryDirectory);
   Unicode_Free(entryFilePath);
   Unicode_Free(memberFilePath);
   Unicode_Free(myValues.memberName);
   free(myValues.locationChecksum);
   free(myValues.executionID);

   return err;
}
Beispiel #27
0
Unicode
File_StripSlashes(ConstUnicode path)  // IN:
{
   Unicode result, volume, dir, base;

   /*
    * SplitName handles all drive letter/UNC/whatever cases, all we
    * have to do is make sure the dir part is stripped of slashes if
    * there isn't a base part.
    */

   File_SplitName(path, &volume, &dir, &base);

   if (!Unicode_IsEmpty(dir) && Unicode_IsEmpty(base)) {
      char *dir2 = Unicode_GetAllocBytes(dir, STRING_ENCODING_UTF8);
      size_t i = strlen(dir2);

      /*
       * Don't strip first slash on Windows, since we want at least
       * one slash to trail a drive letter/colon or UNC specifier.
       */

#if defined(_WIN32)
      while ((i > 1) && (('/' == dir2[i - 1]) ||
                         ('\\' == dir2[i - 1]))) {
#else
      while ((i > 0) && ('/' == dir2[i - 1])) {
#endif
         i--;
      }

      Unicode_Free(dir);
      dir = Unicode_AllocWithLength(dir2, i, STRING_ENCODING_UTF8);
      free(dir2);
   }

   result = Unicode_Join(volume, dir, base, NULL);

   Unicode_Free(volume);
   Unicode_Free(dir);
   Unicode_Free(base);

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * File_MapPathPrefix --
 *
 *      Given a path and a newPrefix -> oldPrefix mapping, transform
 *      oldPath according to the mapping.
 *
 * Results:
 *      The new path, or NULL if there is no mapping.
 *
 * Side effects:
 *      The returned string is allocated, free it.
 *
 *-----------------------------------------------------------------------------
 */

char *
File_MapPathPrefix(const char *oldPath,       // IN:
                   const char **oldPrefixes,  // IN:
                   const char **newPrefixes,  // IN:
                   size_t numPrefixes)        // IN:
{
   int i;
   size_t oldPathLen = strlen(oldPath);

   for (i = 0; i < numPrefixes; i++) {
      char *newPath;
      char *oldPrefix;
      char *newPrefix;
      size_t oldPrefixLen;

      oldPrefix = File_StripSlashes(oldPrefixes[i]);
      newPrefix = File_StripSlashes(newPrefixes[i]);
      oldPrefixLen = strlen(oldPrefix);

      /*
       * If the prefix matches on a DIRSEPS boundary, or the prefix is the
       * whole string, replace it.
       *
       * If we don't insist on matching a whole directory name, we could
       * mess things of if one directory is a substring of another.
       *
       * Perform a case-insensitive compare on Windows. (There are
       * case-insensitive filesystems on MacOS also, but the problem
       * is more acute with Windows because of frequent drive-letter
       * case mismatches. So in lieu of actually asking the
       * filesystem, let's just go with a simple ifdef for now.)
       */

      if ((oldPathLen >= oldPrefixLen) &&
#ifdef _WIN32
          (Str_Strncasecmp(oldPath, oldPrefix, oldPrefixLen) == 0) &&
#else
          (Str_Strncmp(oldPath, oldPrefix, oldPrefixLen) == 0) &&
#endif
          (strchr(VALID_DIRSEPS, oldPath[oldPrefixLen]) ||
              (oldPath[oldPrefixLen] == '\0'))) {
         size_t newPrefixLen = strlen(newPrefix);
         size_t newPathLen = (oldPathLen - oldPrefixLen) + newPrefixLen;

         ASSERT(newPathLen > 0);
         ASSERT(oldPathLen >= oldPrefixLen);

         newPath = Util_SafeMalloc((newPathLen + 1) * sizeof(char));
         memcpy(newPath, newPrefix, newPrefixLen);
         memcpy(newPath + newPrefixLen, oldPath + oldPrefixLen,
                oldPathLen - oldPrefixLen + 1);
         /*
          * It should only match once.  Weird self-referencing mappings
          * aren't allowed.
          */

         free(oldPrefix);
         free(newPrefix);

         return newPath;
      }
      free(oldPrefix);
      free(newPrefix);
   }

   return NULL;
}
static int
ScanDirectory(ConstUnicode lockDir,     // IN:
              int (*func)(              // IN:
                     ConstUnicode lockDir,
                     ConstUnicode fileName,
                     LockValues *memberValues,
                     LockValues *myValues
                   ),
              LockValues *myValues,    // IN:
              Bool cleanUp)            // IN:
{
   uint32 i;
   int err;
   int numEntries;

   Unicode *fileList = NULL;
   char *myExecutionID = NULL;
   char *locationChecksum = NULL;

   ASSERT(lockDir);

   numEntries = FileListDirectoryRobust(lockDir, &fileList);

   if (numEntries == -1) {
      Log(LGPFX" %s: Could not read the directory '%s': %d\n",
          __FUNCTION__, UTF8(lockDir), Err_Errno());

      return EDOM;  // out of my domain
   }

   /* Pass 1: Validate entries and handle any 'D' entries */
   for (i = 0, err = 0; i < numEntries; i++) {
      /* Remove any non-locking files */
      if (!FileLockValidName(fileList[i])) {
         Log(LGPFX" %s discarding %s from %s'; invalid file name.\n",
             __FUNCTION__, UTF8(fileList[i]), UTF8(lockDir));

         err = RemoveLockingFile(lockDir, fileList[i]);
         if (err != 0) {
            goto bail;
         }

        Unicode_Free(fileList[i]);
        fileList[i] = NULL;

        continue;
      }

      /*
       * Any lockers appear to be entering?
       *
       * This should be rather rare. If a locker dies while entering
       * this will cleaned-up.
       */

      if (Unicode_StartsWith(fileList[i], "D")) {
         if (cleanUp) {
            err = ActivateLockList(fileList[i], myValues);
            if (err != 0) {
               goto bail;
            }
        }

        Unicode_Free(fileList[i]);
        fileList[i] = NULL;
      }
   }

   if (myValues->lockList != NULL) {
      goto bail;
   }

   myExecutionID = FileLockGetExecutionID();
   locationChecksum = FileLockLocationChecksum(lockDir);

   /* Pass 2: Handle the 'M' entries */
   for (i = 0, err = 0; i < numEntries; i++) {
      LockValues *ptr;
      Bool       myLockFile;
      LockValues memberValues;
      char       buffer[FILELOCK_DATA_SIZE];

      if ((fileList[i] == NULL) ||
          (Unicode_StartsWith(fileList[i], "E"))) {
         continue;
      }

      myLockFile = (Unicode_Compare(fileList[i],
                          myValues->memberName) == 0) ? TRUE : FALSE;

      if (myLockFile) {
         /* It's me! No need to read or validate anything. */
         ptr = myValues;
      } else {
         /* It's not me! Attempt to extract the member values. */
         err = FileLockMemberValues(lockDir, fileList[i], buffer,
                                    FILELOCK_DATA_SIZE, &memberValues);

         if (err != 0) {
            if (err == ENOENT) {
               err = 0;
               /* Not there anymore; locker unlocked or timed out */
               continue;
            }

            break;
         }

         /* Remove any stale locking files */
         if (FileLockMachineIDMatch(myValues->machineID,
                                    memberValues.machineID)) {
            char *dispose = NULL;

            if (FileLockValidOwner(memberValues.executionID,
                                   memberValues.payload)) {
               /* If it's mine it better still be where I put it! */
               if ((strcmp(myExecutionID, memberValues.executionID) == 0) &&
                   ((memberValues.locationChecksum != NULL) &&
                    (strcmp(memberValues.locationChecksum,
                            locationChecksum) != 0))) {
                  dispose = "lock file has been moved.";
               }
            } else {
               dispose = "invalid executionID.";
            }

            if (dispose) {
               Log(LGPFX" %s discarding %s from %s': %s\n",
                   __FUNCTION__, UTF8(fileList[i]), UTF8(lockDir), dispose);

               Unicode_Free(memberValues.memberName);

               err = RemoveLockingFile(lockDir, fileList[i]);
               if (err != 0) {
                  break;
               }

               continue;
            }
         }

         ptr = &memberValues;
      }

      /* Locking file looks good; see what happens */
      err = (*func)(lockDir, fileList[i], ptr, myValues);

      if (ptr == &memberValues) {
         Unicode_Free(memberValues.memberName);
      }

      if (err != 0) {
         break;
      }
   }

bail:

   for (i = 0; i < numEntries; i++) {
      Unicode_Free(fileList[i]);
   }

   free(fileList);
   free(locationChecksum);
   free(myExecutionID);

   return err;
}
VixError
Vix_TranslateSystemError(int systemError) // IN
{
   VixError err = VIX_E_FAIL;
#ifdef _WIN32
   Unicode msg;

   switch (systemError) {
   case ERROR_ACCESS_DENIED:
      err = VIX_E_FILE_ACCESS_ERROR;
      break;
   case ERROR_INVALID_NAME:
      err = VIX_E_FILE_NAME_INVALID;
      break;
   case ERROR_FILENAME_EXCED_RANGE:
      err = VIX_E_FILE_NAME_TOO_LONG;
      break;
   case ERROR_FILE_NOT_FOUND:
   case ERROR_PATH_NOT_FOUND:
   case ERROR_BAD_PATHNAME:
   case ERROR_DIRECTORY:
   case ERROR_BUFFER_OVERFLOW: 
      err = VIX_E_FILE_NOT_FOUND;
      break;
   case ERROR_DIR_NOT_EMPTY:
      err = VIX_E_DIRECTORY_NOT_EMPTY;
      break;
   case ERROR_TOO_MANY_OPEN_FILES:
   case ERROR_NO_MORE_FILES:
   case ERROR_WRITE_PROTECT:
   case ERROR_WRITE_FAULT:
   case ERROR_READ_FAULT:
   case ERROR_SHARING_VIOLATION:
   case ERROR_SEEK:
   case ERROR_CANNOT_MAKE:
      Log("%s: system error = %d\n", __FUNCTION__,
          systemError);
      err = VIX_E_FILE_ERROR;
      break;
   case ERROR_HANDLE_DISK_FULL:
   case ERROR_DISK_FULL:
      err = VIX_E_DISK_FULL;
      break;
   case ERROR_FILE_EXISTS:
   case ERROR_ALREADY_EXISTS:
      err = VIX_E_FILE_ALREADY_EXISTS;
      break;
   case ERROR_BUSY:
   case ERROR_PATH_BUSY:
      err = VIX_E_OBJECT_IS_BUSY;
      break;
   case ERROR_INVALID_PARAMETER:
      err = VIX_E_INVALID_ARG;
      break;
   case ERROR_NOT_SUPPORTED:
      err = VIX_E_NOT_SUPPORTED;
      break;
   case ERROR_NO_DATA:
   case ERROR_INVALID_DATA: 
      err = VIX_E_NOT_FOUND;
      break;
   case ERROR_NOT_ENOUGH_MEMORY:
      err = VIX_E_OUT_OF_MEMORY;
      break;
   default:
      err = VIX_E_FAIL;
   }
   msg = Win32U_FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
                              NULL, systemError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                              NULL);

   Log("Foundry operation failed with system error: %s (%d), translated to %"FMT64"d\n",
       msg, systemError, err);
   Unicode_Free(msg);

#else // linux, other *nix
   err = Vix_TranslateErrno(systemError);
#endif

   return err;
} // Vix_TranslateSystemError
static int
Scanner(ConstUnicode lockDir,    // IN:
        int (*func)(             // IN:
               ConstUnicode lockDir,
               ConstUnicode fileName,
               LockValues *memberValues,
               LockValues *myValues
            ),
        LockValues *myValues,    // IN:
        Bool cleanUp)            // IN:
{
   int        err;
   ActiveLock *ptr;

   ASSERT(lockDir);

   myValues->lockList = NULL;

   while (TRUE) {
      ActiveLock *prev;

      err = ScanDirectory(lockDir, func, myValues, cleanUp);
      if ((err > 0) || ((err == 0) && (myValues->lockList == NULL))) {
         break;
      }

      prev = NULL;
      ptr = myValues->lockList;

      /*
       * Some 'D' entries have persisted. Age them and remove those that
       * have not progressed. Remove those that have disappeared.
       */

      while (ptr != NULL) {
         Bool remove;

         if (ptr->marked) {
            if (ptr->age > FILELOCK_PROGRESS_DEARTH) {
               Unicode temp;
               Unicode path;
               UnicodeIndex index;

               ASSERT(Unicode_StartsWith(ptr->dirName, "D"));

               Log(LGPFX" %s discarding %s data from '%s'.\n",
                   __FUNCTION__, UTF8(ptr->dirName), UTF8(lockDir));

               path = Unicode_Join(lockDir, DIRSEPS, ptr->dirName, NULL);

               index = Unicode_FindLast(path, "D");
               ASSERT(index != UNICODE_INDEX_NOT_FOUND);

               temp = Unicode_Replace(path, index, 1, "M");
               FileDeletionRobust(temp, FALSE);
               Unicode_Free(temp);

               temp = Unicode_Replace(path, index, 1, "E");
               FileDeletionRobust(temp, FALSE);
               Unicode_Free(temp);

               FileRemoveDirectoryRobust(path);

               Unicode_Free(path);

               remove = TRUE;
            } else {
               ptr->marked = FALSE;
               ptr->age += FILELOCK_PROGRESS_SAMPLE;

               remove = FALSE;
            }
         } else {
            remove = TRUE;
         }

         if (remove) {
            if (prev == NULL) {
               myValues->lockList = ptr->next;
            } else {
               prev->next = ptr->next;
            }
         }

         prev = ptr;
         ptr = ptr->next;
      }

      FileSleeper(FILELOCK_PROGRESS_SAMPLE); // relax
   }

   /* Clean up anything still on the list; they are no longer important */
   while (myValues->lockList != NULL) {
      ptr = myValues->lockList;
      myValues->lockList = ptr->next;

      Unicode_Free(ptr->dirName);

      free(ptr);
   }

   return err;
}