mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
527 lines
16 KiB
527 lines
16 KiB
/*++
|
|
|
|
Copyright (c) 1987-1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
CopyTime.c
|
|
|
|
Abstract:
|
|
|
|
This module contains:
|
|
|
|
ReplCopyJustDateTime
|
|
ReplIsFileTimeCloseEnough
|
|
ReplMungeFileTime
|
|
|
|
Author:
|
|
|
|
JR (John Rogers, JohnRo@Microsoft)
|
|
|
|
Environment:
|
|
|
|
User mode only.
|
|
Contains NT-specific code.
|
|
Requires ANSI C extensions: slash-slash comments, long external names.
|
|
|
|
Revision History:
|
|
|
|
16-Dec-1992 JohnRo
|
|
RAID 1513: Repl does not maintain ACLs. (Also fix HPFS->FAT timestamp.)
|
|
Extracted ReplCopyJustDateTime to its own source file (from syncer.c).
|
|
10-Mar-1993 JohnRo
|
|
RAID 13126: Fix repl memory leak.
|
|
Log errors in ReplCopyJustDateTime() and ReplMungeFileTime().
|
|
Added debug output to catch dir timestamp being set wrong.
|
|
16-Jun-1993 JohnRo
|
|
RAID 13080: Allow repl between different timezones.
|
|
(Actually fix "off by one" problem when munging NTFS to FAT time.)
|
|
09-Jul-1993 JohnRo
|
|
RAID 15736: OS/2 time stamps are broken again (try rounding down).
|
|
|
|
--*/
|
|
|
|
// These must be included first:
|
|
|
|
#include <nt.h> // NtOpenFile(), ULONG, etc.
|
|
#include <ntrtl.h> // PLARGE_INTEGER, TIME_FIELDS, etc.
|
|
#include <nturtl.h> // Needed for ntrtl.h and windows.h to co-exist.
|
|
|
|
#include <windows.h> // GetLastError(), LPFILETIME, CompareFileTime(), etc.
|
|
#include <lmcons.h>
|
|
|
|
// These may be included in any order:
|
|
|
|
#include <client.h> // RCGlobalFsTimeResultionSecs.
|
|
#include <lmerr.h> // NO_ERROR, NERR_InternalError.
|
|
#include <lmerrlog.h> // NELOG_* defines
|
|
#include <netdebug.h> // DBGSTATIC, NetpKdPrint(), FORMAT_ equates, etc.
|
|
#include <netlibnt.h> // NetpNtStatusToApiStatus().
|
|
#include <prefix.h> // PREFIX_ equates.
|
|
#include <repldefs.h> // ReplCopyJustDateTime(), UNKNOWN_FS_RESOLUTION, etc.
|
|
#include <timelib.h> // NetpFileTimeToSecondsSince1970().
|
|
|
|
|
|
#define ISEVEN(n) ( ( (n) & 0x01 ) == 0 )
|
|
|
|
|
|
#ifndef REPL_DEBUG_COPYTIME
|
|
#define REPL_DEBUG_COPYTIME 0x00000800 // Debug the time copy routines.
|
|
#endif
|
|
|
|
|
|
DBGSTATIC NET_API_STATUS
|
|
ReplMungeFileTime(
|
|
IN PLARGE_INTEGER OriginalFileTime,
|
|
IN DWORD ImportFsTimeResolutionSecs,
|
|
OUT PLARGE_INTEGER MungedFileTime
|
|
);
|
|
|
|
|
|
VOID
|
|
ReplCopyJustDateTime(
|
|
IN LPCTSTR SourcePath,
|
|
IN LPCTSTR DestPath,
|
|
IN BOOL IsDirectory
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set the date and time stamps of the destination path to that of the
|
|
source path.
|
|
|
|
This function can be called in different contexts for different types
|
|
of paths:
|
|
|
|
(1) for files (called by ReplCopyFile).
|
|
|
|
(2) for first-level directories (called by ReplFileIntegritySync and
|
|
ReplTreeIntegritySync).
|
|
|
|
(3) for non-first-level directories (called by ReplCopyDirectoryItself).
|
|
In this case, ReplCopyJustDateTime is typically called on a pair of
|
|
directories to restore the date and time after files have been
|
|
added to the directory (changing the timestamps in the first place).
|
|
|
|
Arguments:
|
|
|
|
SourcePath - Specifies the path (of the file or directory) which
|
|
currently has the date and time stamps. This must already exist.
|
|
|
|
DestPath - Specifies the path (of the file or directory) which is to
|
|
receive the date and time stamps. This must already exist.
|
|
|
|
IsDirectory - true iff this is a directory copy; that is,
|
|
SourcePath and DestPath are both directories.
|
|
|
|
Return Value:
|
|
|
|
NONE.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS ApiStatus;
|
|
HANDLE FileHandle = INVALID_HANDLE_VALUE; // Src file, then dest file.
|
|
FILE_BASIC_INFORMATION DestInfo;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
LARGE_INTEGER MungedLastWriteTime;
|
|
NTSTATUS NtStatus;
|
|
OBJECT_ATTRIBUTES ObjectAttributes; // Src obj attr, then dest obj attr.
|
|
const ULONG OpenOptions = // Src or dest open options.
|
|
FILE_SYNCHRONOUS_IO_NONALERT
|
|
| ( IsDirectory ? FILE_DIRECTORY_FILE : 0 );
|
|
BOOL PathAllocated = FALSE;
|
|
FILE_BASIC_INFORMATION SourceInfo;
|
|
UNICODE_STRING UnicodePath;
|
|
|
|
NetpAssert( SourcePath != NULL );
|
|
NetpAssert( DestPath != NULL );
|
|
|
|
//
|
|
// Determine the timestamps on the source path.
|
|
//
|
|
|
|
RtlInitUnicodeString(
|
|
& UnicodePath, // output: struct
|
|
SourcePath ); // input: null terminated
|
|
|
|
if( !RtlDosPathNameToNtPathName_U(
|
|
SourcePath,
|
|
&UnicodePath,
|
|
NULL,
|
|
NULL) ) {
|
|
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplCopyJustDateTime: RtlDosPathNameToNtPathname_U"
|
|
" of source '" FORMAT_LPTSTR "' failed.\n", SourcePath ));
|
|
|
|
// BUGBUG: this is just our best guess for an error code for this.
|
|
ApiStatus = NERR_InternalError;
|
|
goto Cleanup;
|
|
}
|
|
PathAllocated = TRUE;
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
(LPVOID) &UnicodePath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL );
|
|
//
|
|
// Open source file, so we can get the timestamps.
|
|
// BUGBUG: This used to ask for SYNCHRONIZE | FILE_READ_ATTRIBUTES,
|
|
// But that ran into a "feature" in the NT redir where it "optimizes"
|
|
// and only gives time in 2 second resolution.
|
|
//
|
|
NtStatus = NtOpenFile(
|
|
& FileHandle,
|
|
SYNCHRONIZE | // desired ...
|
|
FILE_READ_ATTRIBUTES | // ... (See BUGBUG above.)
|
|
FILE_READ_DATA, // ... access
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ, // share access
|
|
OpenOptions );
|
|
|
|
if ( !NT_SUCCESS( NtStatus ) ) {
|
|
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplCopyJustDateTime: NtOpenFile of source '"
|
|
FORMAT_LPTSTR "' gave NT status " FORMAT_NTSTATUS ".\n",
|
|
SourcePath, NtStatus ));
|
|
|
|
ApiStatus = NetpNtStatusToApiStatus( NtStatus );
|
|
NetpAssert( ApiStatus != NO_ERROR );
|
|
goto Cleanup;
|
|
}
|
|
NetpAssert( NtStatus == STATUS_SUCCESS );
|
|
|
|
NtStatus = NtQueryInformationFile(
|
|
FileHandle,
|
|
&IoStatusBlock,
|
|
(PVOID) &SourceInfo,
|
|
(ULONG) sizeof(SourceInfo),
|
|
FileBasicInformation ); // info class
|
|
|
|
if ( !NT_SUCCESS( NtStatus ) ) {
|
|
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplCopyJustDateTime: NtQueryInformationFile (src) failed, "
|
|
" NT status is " FORMAT_NTSTATUS
|
|
".\n", NtStatus ));
|
|
|
|
ApiStatus = NetpNtStatusToApiStatus( NtStatus );
|
|
NetpAssert( ApiStatus != NO_ERROR );
|
|
goto Cleanup;
|
|
}
|
|
NetpAssert( NtStatus == STATUS_SUCCESS );
|
|
|
|
IF_DEBUG( COPYTIME ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"initial master time for " FORMAT_LPTSTR " is:\n ",
|
|
SourcePath ));
|
|
NetpDbgDisplayLargeIntegerTime( &(SourceInfo.LastWriteTime) );
|
|
NetpKdPrint((
|
|
", filetime(low)=" FORMAT_HEX_DWORD ", "
|
|
"filetime(high)=" FORMAT_HEX_DWORD ".\n",
|
|
(DWORD) SourceInfo.LastWriteTime.LowPart,
|
|
(DWORD) SourceInfo.LastWriteTime.HighPart ));
|
|
}
|
|
|
|
(void) NtClose( FileHandle );
|
|
FileHandle = INVALID_HANDLE_VALUE;
|
|
|
|
(VOID) RtlFreeHeap( RtlProcessHeap(), 0, UnicodePath.Buffer );
|
|
PathAllocated = FALSE;
|
|
|
|
//
|
|
// OK. We need to muck with a timestamp here. The situation involves
|
|
// different precisions for different filesystems. HPFS (under NT and OS/2)
|
|
// has 1-second precision. FAT (under any O.S.) has 2-second precision.
|
|
// The replicator (e.g. under OS/2) computes checksums using the FAT-format
|
|
// of the timestamp. In this case, OS/2 seems to be truncating the time.
|
|
// Under NT, there is a different policy: round the time up, to make
|
|
// life easy for tree-copy-by-time (TC /t). So, in order to interoperate
|
|
// with OS/2 systems, we have to munge them overselves.
|
|
//
|
|
// Note that only the last write time is involved in the checksum. If
|
|
// the other times were also involved, then we would have to munge them
|
|
// here too.
|
|
//
|
|
ApiStatus = ReplMungeFileTime(
|
|
& (SourceInfo.LastWriteTime), // input: original time
|
|
RCGlobalFsTimeResolutionSecs, // input: import filesys res.
|
|
& MungedLastWriteTime ); // output: munged time
|
|
if (ApiStatus != NO_ERROR) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplCopyJustDateTime: ReplMungeFileTime failed, status "
|
|
FORMAT_API_STATUS ".\n", ApiStatus ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Set the timestamps on the dest path.
|
|
//
|
|
|
|
RtlInitUnicodeString(
|
|
& UnicodePath, // output: struct
|
|
DestPath ); // input: null terminated
|
|
|
|
if( !RtlDosPathNameToNtPathName_U(
|
|
DestPath,
|
|
&UnicodePath,
|
|
NULL,
|
|
NULL) ) {
|
|
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplCopyJustDateTime: RtlDosPathNameToNtPathname_U"
|
|
" of dest '" FORMAT_LPTSTR "' failed.\n", DestPath ));
|
|
|
|
ApiStatus = NERR_InternalError; // BUGBUG: better error code?
|
|
goto Cleanup;
|
|
}
|
|
PathAllocated = TRUE;
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
(LPVOID) &UnicodePath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL );
|
|
|
|
NtStatus = NtOpenFile(
|
|
& FileHandle,
|
|
SYNCHRONIZE | // desired ...
|
|
FILE_READ_ATTRIBUTES | // ...
|
|
FILE_WRITE_ATTRIBUTES, // ... access
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
0, // share access (none)
|
|
OpenOptions );
|
|
|
|
if ( !NT_SUCCESS( NtStatus ) ) {
|
|
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplCopyJustDateTime: NtOpenFile of dest '"
|
|
FORMAT_LPTSTR "' gave NT status " FORMAT_NTSTATUS ".\n",
|
|
DestPath, NtStatus ));
|
|
|
|
ApiStatus = NetpNtStatusToApiStatus( NtStatus );
|
|
NetpAssert( ApiStatus != NO_ERROR );
|
|
goto Cleanup;
|
|
}
|
|
NetpAssert( NtStatus == STATUS_SUCCESS );
|
|
|
|
NtStatus = NtQueryInformationFile(
|
|
FileHandle,
|
|
&IoStatusBlock,
|
|
(PVOID) &DestInfo,
|
|
(ULONG) sizeof(DestInfo),
|
|
FileBasicInformation ); // info class
|
|
|
|
if ( !NT_SUCCESS( NtStatus ) ) {
|
|
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplCopyJustDateTime: NtQueryInformationFile (dest) failed, "
|
|
" NT status is " FORMAT_NTSTATUS ".\n", NtStatus ));
|
|
|
|
ApiStatus = NetpNtStatusToApiStatus( NtStatus );
|
|
NetpAssert( ApiStatus != NO_ERROR );
|
|
goto Cleanup;
|
|
}
|
|
NetpAssert( NtStatus == STATUS_SUCCESS );
|
|
|
|
//
|
|
// We only checksum the write time, but let's update them all
|
|
// just to be kosher. Note that last write time must be the munged
|
|
// version, or client's checksum will never match NT server's.
|
|
//
|
|
DestInfo.ChangeTime = SourceInfo.ChangeTime;
|
|
DestInfo.CreationTime = SourceInfo.CreationTime;
|
|
DestInfo.LastAccessTime = SourceInfo.LastAccessTime;
|
|
DestInfo.LastWriteTime = MungedLastWriteTime;
|
|
|
|
NtStatus = NtSetInformationFile(
|
|
FileHandle,
|
|
&IoStatusBlock,
|
|
(PVOID) &DestInfo,
|
|
sizeof(DestInfo),
|
|
FileBasicInformation ); // info class
|
|
|
|
if ( !NT_SUCCESS( NtStatus ) ) {
|
|
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplCopyJustDateTime: NtSetInformationFile failed "
|
|
FORMAT_NTSTATUS ".\n", NtStatus ));
|
|
|
|
ApiStatus = NetpNtStatusToApiStatus( NtStatus );
|
|
NetpAssert( ApiStatus != NO_ERROR );
|
|
goto Cleanup;
|
|
}
|
|
NetpAssert( NtStatus == STATUS_SUCCESS );
|
|
|
|
IF_DEBUG( COPYTIME ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"Changed time for " FORMAT_LPTSTR " to ",
|
|
DestPath ));
|
|
NetpDbgDisplayLargeIntegerTime( &(DestInfo.LastWriteTime) );
|
|
NetpKdPrint(( "\n" ));
|
|
}
|
|
|
|
ApiStatus = NO_ERROR;
|
|
|
|
|
|
Cleanup:
|
|
|
|
if (ApiStatus != NO_ERROR) {
|
|
|
|
//
|
|
// Log the error.
|
|
// BUGBUG: extract master server name and log there too.
|
|
//
|
|
ReplErrorLog(
|
|
NULL, // no server name (log locally)
|
|
NELOG_ReplSysErr, // log code
|
|
ApiStatus,
|
|
NULL, // optional str1
|
|
NULL); // optional str2
|
|
}
|
|
|
|
if (FileHandle != INVALID_HANDLE_VALUE) {
|
|
(VOID) NtClose( FileHandle );
|
|
}
|
|
|
|
if (PathAllocated) {
|
|
(VOID) RtlFreeHeap( RtlProcessHeap(), 0, UnicodePath.Buffer );
|
|
}
|
|
|
|
return;
|
|
|
|
} // ReplCopyJustDateTime
|
|
|
|
|
|
DBGSTATIC NET_API_STATUS
|
|
ReplMungeFileTime(
|
|
IN PLARGE_INTEGER OriginalFileTime,
|
|
IN DWORD ImportFsTimeResolutionSecs,
|
|
OUT PLARGE_INTEGER MungedFileTime
|
|
)
|
|
{
|
|
ULONG MungedSecondsSince1970;
|
|
ULONG SecondsSince1970;
|
|
FILETIME TempFileTime;
|
|
|
|
UNREFERENCED_PARAMETER( ImportFsTimeResolutionSecs );
|
|
|
|
NetpAssert( OriginalFileTime != NULL );
|
|
NetpAssert( MungedFileTime != NULL );
|
|
|
|
//
|
|
// Convert from 64-bit to seconds since 1970.
|
|
// Round up to next second here.
|
|
//
|
|
TempFileTime.dwLowDateTime = OriginalFileTime->LowPart;
|
|
TempFileTime.dwHighDateTime = OriginalFileTime->HighPart;
|
|
|
|
NetpFileTimeToSecondsSince1970 (
|
|
&TempFileTime, // input (64-bit)
|
|
&SecondsSince1970 ); // output (secs) (round up to next sec)
|
|
|
|
//
|
|
// Round up to 2 second inteval.
|
|
//
|
|
if ( !ISEVEN( SecondsSince1970 ) ) {
|
|
MungedSecondsSince1970 = SecondsSince1970 + 1;
|
|
} else {
|
|
MungedSecondsSince1970 = SecondsSince1970;
|
|
}
|
|
NetpAssert( ISEVEN( MungedSecondsSince1970 ) );
|
|
|
|
//
|
|
// Convert back to 64-bit format.
|
|
//
|
|
(VOID) RtlSecondsSince1970ToTime(
|
|
MungedSecondsSince1970, // input: secs (rounded to 2 secs)
|
|
MungedFileTime ); // output: 64-bit
|
|
|
|
return (NO_ERROR);
|
|
|
|
} // ReplMungeFileTime
|
|
|
|
|
|
BOOL
|
|
ReplIsFileTimeCloseEnough(
|
|
IN LPVOID MasterFileTime, // Points to a FILETIME value.
|
|
IN LPVOID ClientFileTime // Points to a FILETIME value.
|
|
)
|
|
{
|
|
LPFILETIME ClientBigTime = ClientFileTime;
|
|
LONG CompareResult; // -1, 0, or 1
|
|
LPFILETIME MasterBigTime = MasterFileTime;
|
|
|
|
NetpAssert( MasterBigTime != NULL );
|
|
NetpAssert( ClientBigTime != NULL );
|
|
|
|
CompareResult = CompareFileTime(
|
|
MasterBigTime,
|
|
ClientBigTime );
|
|
|
|
if (CompareResult == 0) {
|
|
|
|
// Times match exactly.
|
|
IF_DEBUG( COPYTIME ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplIsFileTimeCloseEnough: "
|
|
"CompareFileTime says files match.\n" ));
|
|
}
|
|
return (TRUE);
|
|
|
|
} else if (CompareResult == 1) {
|
|
|
|
// Client time too low. Not close enough.
|
|
return (FALSE);
|
|
|
|
} else {
|
|
|
|
DWORD MasterSecondsSince1970;
|
|
DWORD ClientSecondsSince1970;
|
|
DWORD TimeDiffSecs;
|
|
|
|
// Master time too low. Because of diff file system resolution
|
|
// or something else?
|
|
NetpAssert( CompareResult == -1 );
|
|
|
|
NetpFileTimeToSecondsSince1970(
|
|
MasterBigTime, // in: 64-bit
|
|
&MasterSecondsSince1970 ); // out: seconds since 1970
|
|
|
|
NetpFileTimeToSecondsSince1970(
|
|
ClientBigTime, // in: 64-bit
|
|
&ClientSecondsSince1970 ); // out: seconds since 1970
|
|
|
|
IF_DEBUG( COPYTIME ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplIsFileTimeCloseEnough: "
|
|
"mast secs since 1970 is " FORMAT_DWORD ", "
|
|
"client secs since 1970 is " FORMAT_DWORD ".\n",
|
|
MasterSecondsSince1970, ClientSecondsSince1970 ));
|
|
}
|
|
|
|
NetpAssert( MasterSecondsSince1970 <= ClientSecondsSince1970 );
|
|
TimeDiffSecs = ClientSecondsSince1970 - MasterSecondsSince1970;
|
|
// NetpAssert( TimeDiffSecs != 0 );
|
|
|
|
// BUGBUG: This used to use filesys resolution
|
|
if (TimeDiffSecs < 2) {
|
|
// if (TimeDiffSecs < RCGlobalFsTimeResolutionSecs) {
|
|
return (TRUE); // Close enough.
|
|
} else {
|
|
return (FALSE); // Not within file system resolution.
|
|
}
|
|
|
|
}
|
|
|
|
/*NOTREACHED*/
|
|
|
|
} // ReplIsFileTimeCloseEnough
|