BUG: ReplaceFile() Requires WRITE_DAC Access on Replacement File (274487)



The information in this article applies to:

  • Microsoft Win32 Application Programming Interface (API), when used with:
    • the operating system: Microsoft Windows 2000

This article was previously published under Q274487

SYMPTOMS

The ReplaceFile function in Windows 2000 replaces one file (the "replaced file") with another file (the "replacement file"). This function doesn't work if the calling user does not have access to open the replacement file with WRITE_DAC permission. This situation is likely to occur if the replacement file resides on an NTFS network share to which the user has only change and/or read access.

CAUSE

To obtain WRITE_DAC access for a file on a network share, either the caller must have full control over the share or the caller must be an administrator on the remote server.

RESOLUTION

To work around this problem, an application can implement its own "Replace File" routine and not specify WRITE_DAC access when it opens the replacement file. Following is sample code to demonstrate this approach.

Sample Code

The following sample code implements a simple replace file function that does not require WRITE_DAC access to the replacement file. Note that this routine does not have all the features of the Windows 2000 ReplaceFile function. That is, this routine does not preserve the following attributes of the original file:
  • Short file name
  • Object identifier
  • DACLs
  • Encryption
  • Compression
  • Named streams not already in the replacement file
BOOL SimpleReplaceFile(LPTSTR szReplaced, LPTSTR szReplacement) {

   HANDLE hReplaced = INVALID_HANDLE_VALUE;
   BOOL   fSuccess  = FALSE;

   WIN32_FILE_ATTRIBUTE_DATA fad;

   __try {

      // Validate parameters.
      if (szReplaced == NULL || szReplacement == NULL) {
         SetLastError(ERROR_INVALID_PARAMETER);
         __leave;
      }

      // Retrieve attributes from the file to be replaced.
      if (!GetFileAttributesEx(szReplaced, GetFileExInfoStandard, &fad))
         __leave;

      // Delete the file that is being replaced.
      if (!DeleteFile(szReplaced))
         __leave;

      // Rename the replacement file.
      if (!MoveFile(szReplacement, szReplaced))
         __leave;

      // This is enough to report success.
      fSuccess = TRUE;

      // Try to preserve the following attributes from the original file:
      fad.dwFileAttributes &= FILE_ATTRIBUTE_ARCHIVE 
            | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NORMAL
            | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED
            | FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM
            | FILE_ATTRIBUTE_TEMPORARY;
      if (!SetFileAttributes(szReplaced, fad.dwFileAttributes))
         __leave;

      // Try to set the creation time to match the original file.
      hReplaced = CreateFile(szReplaced, 0, FILE_SHARE_READ 
            | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
            NULL, OPEN_EXISTING, 0, NULL);
      if (hReplaced == NULL)
         __leave;

      if (!SetFileTime(hReplaced, &(fad.ftCreationTime), NULL, NULL))
         __leave;
   
   } __finally {

     if (hReplaced != INVALID_HANDLE_VALUE)
         CloseHandle(hReplaced);
   }

   return fSuccess;
}
				

STATUS

Microsoft has confirmed that this is a bug in the Microsoft products that are listed at the beginning of this article.

Modification Type:MajorLast Reviewed:2/25/2004
Keywords:kbAPI kbBug kbFileIO kbKernBase kbpending kbSecurity KB274487