/*** fileutil.c - Utility routines for dealing with files * * Microsoft Confidential * Copyright (C) Microsoft Corporation 1993-1997 * All Rights Reserved. * * Author: * Benjamin W. Slivka * * History: * 20-Feb-1994 bens Initial version (code from diamond.c) * 21-Feb-1994 bens Add IsWildPath() * 24-Feb-1994 bens Added tempfile functions * 23-Mar-1994 bens Added Win32<->MS-DOS file attribute mapping * 03-Jun-1994 bens VER.DLL support * 07-Jun-1994 bens Move VER.DLL stuff to filever.c * 14-Dec-1994 bens Fix bug in IsWildPath() * 02-Feb-1996 msliger Reduced bogosity in pattern matcher * 26-Feb-1997 msliger Avoid NULL deref in catDirAndFile() * 04-Mar-1997 msliger Close file before applying attributes to avoid * setting the archive bit. * 01-Apr-1997 msliger Avoid bounds error in ensureDirectory. */ #include #include #include #include #include #include #include #include #include #include #include #ifdef BIT16 #include #else // !BIT16 //** Get minimal Win32 definitions #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #undef ERROR // Override "#define ERROR 0" in wingdi.h #endif // !BIT16 #include "types.h" #include "asrt.h" #include "error.h" #include "mem.h" #include "message.h" #include "fileutil.h" #include // LOCALIZED for EXTRACT.EXE -- specify "cl /Ipath" /** TEMPFILE definitions * */ typedef struct { /* tmp */ #ifdef ASSERT SIGNATURE sig; // structure signature sigTEMPFILE #endif FILE *pfile; // Stream pointer (fopen,fread,fwrite,fclose,...) char *pszFile; // Constructed filename (MemFree to free) char *pszDesc; // Description of tempfile } TEMPFILE; typedef TEMPFILE *PTEMPFILE; /* ptmp */ #ifdef ASSERT #define sigTEMPFILE MAKESIG('T','M','P','F') // TEMPFILE signature #define AssertTmp(ptmp) AssertStructure(ptmp,sigTEMPFILE); #else // !ASSERT #define AssertTmp(ptmp) #endif // !ASSERT #define PTMPfromHTMP(htmp) ((PTEMPFILE)(htmp)) #define HTMPfromPTMP(ptmp) ((HTEMPFILE)(ptmp)) #ifdef BIT16 #define CharIncr(psz) (psz = psz + 1) #else #define CharIncr(psz) (psz = CharNextExA(CP_ACP, psz, 0)) #endif /*** TmpCreate - Create a temporary file * * NOTE: See fileutil.h for entry/exit conditions. */ HTEMPFILE TmpCreate(char *pszDesc, char *pszPrefix, char *pszMode, PERROR perr) { #define cTMP_RETRY 5 // Number of tempfile retries int cFailure=0; FILE *pfile=NULL; char *pszTmpName; PTEMPFILE ptmp; //** Try to create a temp file (give it 6 tries for good luck) while (pfile == NULL) { pszTmpName = _tempnam("",pszPrefix); // Get a name if (pszTmpName != NULL) { pfile = fopen(pszTmpName,pszMode); // Create the file } if (pfile == NULL) { // Name or create failed cFailure++; // Count failures if (cFailure > cTMP_RETRY) { // Failure, select error message if (pszTmpName == NULL) { // Name create failed ErrSet(perr,pszFILERR_CANT_CREATE_TMP,"%s",pszDesc); } else { // File create failed ErrSet(perr,pszFILERR_CANT_CREATE_FILE,"%s%s", pszDesc,pszTmpName); free(pszTmpName); //BC6 cr } return NULL; } if (pszTmpName != NULL) { //BC6 cr free(pszTmpName); } } } //** File create worked, allocate our tempfile structure and fill it in if (!(ptmp = MemAlloc(sizeof(TEMPFILE)))) { ErrSet(perr,pszFILERR_OUT_OF_MEMORY,"%s",pszDesc); goto error; } ptmp->pszFile = NULL; ptmp->pszDesc = NULL; if (!(ptmp->pszFile = MemStrDup(pszTmpName))) { ErrSet(perr,pszFILERR_OUT_OF_MEMORY,"%s",pszDesc); goto error; } if (!(ptmp->pszDesc = MemStrDup(pszDesc))) { ErrSet(perr,pszFILERR_OUT_OF_MEMORY,"%s",pszDesc); goto error; } ptmp->pfile = pfile; SetAssertSignature(ptmp,sigTEMPFILE); free(pszTmpName); //BC6 return HTMPfromPTMP(ptmp); // Success error: if (ptmp) { if (ptmp->pszDesc != NULL) { MemFree(ptmp->pszDesc); } if (ptmp->pszFile != NULL) { MemFree(ptmp->pszFile); } MemFree(ptmp); } fclose(pfile); free(pszTmpName); return NULL; // Failure } /* createTempFile() */ /*** TmpGetStream - Get FILE* from HTEMPFILE, to perform I/O * * NOTE: See fileutil.h for entry/exit conditions. */ FILE *TmpGetStream(HTEMPFILE htmp, PERROR perr) { PTEMPFILE ptmp; ptmp = PTMPfromHTMP(htmp); AssertTmp(ptmp); return ptmp->pfile; } /* TmpGetStream() */ /*** TmpGetDescription - Get description of temporary file * * NOTE: See fileutil.h for entry/exit conditions. */ char *TmpGetDescription(HTEMPFILE htmp, PERROR perr) { PTEMPFILE ptmp; ptmp = PTMPfromHTMP(htmp); AssertTmp(ptmp); return ptmp->pszDesc; } /* TmpGetDescription() */ /*** TmpGetFileName - Get filename of temporary file * * NOTE: See fileutil.h for entry/exit conditions. */ char *TmpGetFileName(HTEMPFILE htmp, PERROR perr) { PTEMPFILE ptmp; ptmp = PTMPfromHTMP(htmp); AssertTmp(ptmp); return ptmp->pszFile; } /* TmpGetFileName() */ /*** TmpClose - Close a temporary file stream, but keep tempfile handle * * NOTE: See fileutil.h for entry/exit conditions. */ BOOL TmpClose(HTEMPFILE htmp, PERROR perr) { PTEMPFILE ptmp; ptmp = PTMPfromHTMP(htmp); AssertTmp(ptmp); //** Only close if it is open if (ptmp->pfile != NULL) { if (fclose(ptmp->pfile) == EOF) { // Close it ErrSet(perr,pszFILERR_CANT_CLOSE_TMP,ptmp->pszDesc); return FALSE; } ptmp->pfile = NULL; // Remember stream is closed } return TRUE; } /* TmpClose() */ /*** TmpOpen - Open the stream for a temporary file * * NOTE: See fileutil.h for entry/exit conditions. * Entry: * htmp - Handle to temp file * pszMode - Mode string passed to fopen ("wt", "wb", "rt", etc.) * perr - ERROR structure * * Exit-Success: * Returns TRUE; stream opened * * Exit-Failure: * Returns NULL; perr filled in */ BOOL TmpOpen(HTEMPFILE htmp, char *pszMode, PERROR perr) { PTEMPFILE ptmp; ptmp = PTMPfromHTMP(htmp); AssertTmp(ptmp); Assert(ptmp->pfile == NULL); // Can't open if already open ptmp->pfile = fopen(ptmp->pszFile,pszMode); // Open the file if (!ptmp->pfile) { ErrSet(perr,pszFILERR_CANNOT_OPEN_TMP,"%s%s", ptmp->pszDesc,ptmp->pszFile); } return (ptmp->pfile != NULL); // Indicate success/failure } /* TmpOpen() */ /*** TmpDestroy - Delete tempfil and destroy handle * * NOTE: See fileutil.h for entry/exit conditions. */ BOOL TmpDestroy(HTEMPFILE htmp, PERROR perr) { PTEMPFILE ptmp; ptmp = PTMPfromHTMP(htmp); AssertTmp(ptmp); //** Make sure file is closed if (ptmp->pfile != NULL) { fclose(ptmp->pfile); } //** Delete tempfile if (remove(ptmp->pszFile) != 0) { ErrSet(perr,pszFILERR_CANT_DELETE_TMP,"%s%s", ptmp->pszDesc,ptmp->pszFile); } //** Free Memory if (ptmp->pszDesc != NULL) { MemFree(ptmp->pszDesc); } if (ptmp->pszFile != NULL) { MemFree(ptmp->pszFile); } ClearAssertSignature(ptmp); MemFree(ptmp); //** Success return TRUE; } /* TmpDestroy() */ /*** getFileSize - Get size of a file * * NOTE: See fileutil.h for entry/exit conditions. */ long getFileSize(char *pszFile, PERROR perr) { struct _stat statbuf; // Buffer for _stat() //** Get file status if (_stat(pszFile,&statbuf) == -1) { //** Could not get file status ErrSet(perr,pszFILERR_FILE_NOT_FOUND,"%s",pszFile); return -1; } //** Make sure it is a file if (statbuf.st_mode & (_S_IFCHR | _S_IFDIR)) { // device or directory ErrSet(perr,pszFILERR_NOT_A_FILE,"%s",pszFile); return -1; } //** Success return statbuf.st_size; } /* getFileSize() */ /*** appendPathSeparator - Append a path separator only if necessary * * NOTE: See fileutil.h for entry/exit conditions. */ int appendPathSeparator(char *pszPathEnd) { //** Add path separator if necessary if ((*pszPathEnd != '\0') && // Path is not empty (*pszPathEnd != chPATH_SEP1) && // Not a path separator (*pszPathEnd != chPATH_SEP2) && // Not a path separator (*pszPathEnd != chDRIVE_SEP) ) { // Not a drive separator *(++pszPathEnd) = chPATH_SEP1; // Add path separator *(++pszPathEnd) = '\0'; // Terminate path return 1; // Account for path separator } //** No separator added return 0; } /* appendPathSeparator() */ /*** catDirAndFile - Concatenate a possibly empty dir and file name * * NOTE: See fileutil.h for entry/exit conditions. */ BOOL catDirAndFile(char * pszResult, int cbResult, char * pszDir, char * pszFile, char * pszFileDef, PERROR perr) { int cch; char *pch; //FEATURE: 14-Feb-1994 bens Need to add pszName to say what field was bad //** Handle directory pszResult[0] = '\0'; // No filespec, yet cch = strlen(pszDir); // Get length of dir if (cch != 0) { // Have to concatenate path strcpy(pszResult,pszDir); // Copy destination dir to buffer cbResult -= cch; // Account for dir //** Add path separator if necessary, adjust remaining size cbResult -= appendPathSeparator(&(pszResult[cch-1])); if (cbResult <= 0) { ErrSet(perr,pszFILERR_PATH_TOO_LONG,"%s",pszDir); return FALSE; } } //** Append file name, using default if primary one not supplied if (*pszFile == '\0') { // Need to construct file name if ((pszFileDef == NULL) || // Don't deref NULL (*pszFileDef == '\0')) { // Empty default name, too return TRUE; // We're done! } pch = getJustFileNameAndExt(pszFileDef,perr); // Get default name if (pch == NULL) { return FALSE; // perr already filled in } } else { pch = pszFile; // Use supplied name } strcat(pszResult,pch); // Append file name cbResult -= strlen(pch); // Update remaining size if (cbResult <= 0) { ErrSet(perr,pszFILERR_PATH_TOO_LONG,"%s",pch); return FALSE; } //** Success return TRUE; } /* catDirAndFile() */ /*** ensureDirectory - Ensure directory exists (creating as needed) * * NOTE: See fileutil.h for entry/exit conditions. */ BOOL ensureDirectory(char *pszPath, BOOL fHasFileName, PERROR perr) { char achDir[cbFILE_NAME_MAX]; // Partial directory buffer int cErrors; int cch; int cchNoPathSep; int i; // Temp file name count int fh; // File handle char *pch; char *pchCurr; // Current path separator char *pchNext; // Next path separator //** Find first path separator, if any. // NOTE: Have to handle case of "d:foo" specially! // for (pch=pszPath; *pch && // Not end of string (*pch!=chPATH_SEP1) && // Not path separator 1 (*pch!=chPATH_SEP2) && // Not path separator 2 ((*pch!=chDRIVE_SEP) || ((*(pch+1)==chPATH_SEP1) || // Not "d:\" (*(pch+1)==chPATH_SEP2))); CharIncr(pch)) { ; // } //** Set correct starting point for first directory component (if any) achDir[0] = '\0'; // Assume current directory if ((*pch == '\0') && // No path separators fHasFileName) { // Just a file name //** Do nothing; for loop below will be skipped because *pch == \0 } else { //** Have to consider whole path pch = pszPath; // Need to ensure directories } //** Make sure directories on path exist (create them all) // We need to identify successive components and create directory // tree one component at a time. Since the directory may already // exist, we do the final test of creating a file there to make // sure we can do the write. for (pchCurr=pch, pchNext=pch; // Process path components *pchNext && *pchCurr; // Until no more //BC6 pchCurr=pchNext+1) { // Skip over last path separator //** Find next path separator for (pch=pchCurr; *pch && (*pch!=chPATH_SEP1) && (*pch!=chPATH_SEP2) && ((*pch!=chDRIVE_SEP) || ((*(pch+1)==chPATH_SEP1) || // Not "d:\" (*(pch+1)==chPATH_SEP2))); CharIncr(pch)) { ; // } pchNext = pch; // Location of next path separator //** Don't process last component if caller said it was a filename if ((*pchNext != '\0') || !fHasFileName) { //** We have a partial path; make sure directory exists cch = (int)(pchNext - pszPath); // Length of partial path if ((cch>0) && ((*pchNext == chDRIVE_SEP) || (*(pchNext-1) == chDRIVE_SEP))) { //** Have "d:xxx" or "d:\xxx", so need to include ":" or "\"! cch++; } strncpy(achDir,pszPath,cch); achDir[cch] = '\0'; // Terminate path _mkdir(achDir); // Ignore any error } } //** Check for special case of root directory: "\" or "\xxx.yyy" if ((strlen(achDir) == 0) && (strlen(pszPath) > 0) && ((*pszPath == chPATH_SEP1) || (*pszPath == chPATH_SEP2))) { achDir[0] = *pszPath; achDir[1] = '\0'; } //** Make sure there is an appropriate separator cch = strlen(achDir); cchNoPathSep = cch; // For error reporting if (cch > 0) { cch += appendPathSeparator(&(achDir[cch-1])); } //** Make sure we can write to the directory // achDir = Has path of directory to test cErrors = 0; // No errors, so far for (i=0; i<999; i++) { //** Form full file name sprintf(&achDir[cch],"CAB%5.5d.TMP",GetCurrentProcessId()+i); //** Make sure file does not exist, and can be created and written to fh = _open(achDir, _O_CREAT | _O_EXCL | _O_RDWR, // Must not exist, read/write _S_IREAD | _S_IWRITE); // Read & Write permission //** Figure out what happened if (fh == -1) { switch (errno) { case EACCES: // Was a dir, or read-only cErrors++; if (cErrors < 5) { // Tolerate this a few times continue; // Try next temp file name } achDir[cchNoPathSep] = '\0'; // Remove temp file name ErrSet(perr,pszFILERR_DIR_NOT_WRITEABLE,"%s",achDir); return FALSE; case EEXIST: // File already exists -- good sign! continue; // Try next temp file name case EMFILE: // Out of file handles achDir[cchNoPathSep] = '\0'; // Remove temp file name ErrSet(perr,pszFILERR_NO_MORE_FILE_HANDLES,"%s",achDir); return FALSE; case EINVAL: // oflag and/or pmode args are bad if (_doserrno == ERROR_DELETE_PENDING) { continue; } // fall through case ENOENT: // File/Path not found default: printf("EnsureDirectory: Cant create file: %s, errno=%d, _doserrno=%d, GLE=%d\n", achDir, errno, _doserrno, GetLastError() ); achDir[cchNoPathSep] = '\0'; // Remove temp file name ErrSet(perr,pszFILERR_CANT_MAKE_DIR,"%s%d%d", achDir, errno, _doserrno); return FALSE; } } //** File was created, close it, delete it, and we're golden _close(fh); // Done with file _unlink(achDir); // Get rid of it return TRUE; // Success } //** Ran out of temp file names achDir[cchNoPathSep] = '\0'; // Remove temp file name ErrSet(perr,pszFILERR_OUT_OF_TMP_FILE_NAMES,"%d%s",i,achDir); return FALSE; } /* ensureDirectory() */ /*** ensureFile - Ensure a file can be created * * NOTE: See fileutil.h for entry/exit conditions. */ BOOL ensureFile(char *pszFile, char *pszDesc, PERROR perr) { int fh; //** Make sure directory is present if (!ensureDirectory(pszFile,TRUE,perr)) { //** Override error message with more meaningful one ErrSet(perr,pszFILERR_CANT_CREATE_FILE,"%s%s",pszDesc,pszFile); return FALSE; } //** Make sure file can be created fh = _open(pszFile, _O_CREAT | _O_RDWR, // Create if necessary, read/write _S_IREAD | _S_IWRITE); // Read & Write permission if (fh == -1) { switch (errno) { case EMFILE: // Out of file handles ErrSet(perr,pszFILERR_NO_MORE_FILE_HANDLES,"%s",pszFile); return FALSE; case EACCES: // Was a dir, or read-only case ENOENT: // File/Path not found case EINVAL: // oflag and/or pmode args are bad default: ErrSet(perr,pszFILERR_CANT_CREATE_FILE,"%s%s",pszDesc,pszFile); return FALSE; } } //** File was created; close it, delete it, and we're golden _close(fh); // Done with file _unlink(pszFile); // Get rid of it return TRUE; } /* ensureFile() */ /*** getJustFileNameAndExt - Get last component in filespec * * NOTE: See fileutil.h for entry/exit conditions. */ char *getJustFileNameAndExt(char *pszPath, PERROR perr) { char *pch=pszPath; char *pchStart=pszPath; // Assume filespec is just a name[.ext] //** Find last path separator while (*pch) { switch (*pch) { case chPATH_SEP1: case chPATH_SEP2: case chDRIVE_SEP: pchStart = pch+1; // Name starts after path/drive separator break; } CharIncr(pch); // Check next character } //** Make sure file name is not empty if (*pchStart == '\0') { // Empty file name ErrSet(perr,pszFILERR_EMPTY_FILE_NAME,"%s",pszPath); return NULL; // Failure } else { return pchStart; // Success } } /* getJustFileNameAndExt() */ /*** IsWildMatch - Test filespec against wild card specification * * NOTE: See fileutil.h for entry/exit conditions. */ BOOL IsWildMatch(char *pszPath, char *pszWild, PERROR perr) { char chNext; char *psz; char *psz1; // Walks through filespec char *psz2; // Walks through pattern // 10/24/96 jforbes Make *.* match everything, even i.have.many.dots if (!strcmp(pszWild, pszALL_FILES)) return TRUE; psz1 = pszPath; // Filespec to test psz2 = pszWild; // Test pattern // While there is pattern to account for keep going while (*psz2) { switch (*psz2) { // Handle wild card chars in pattern case chWILD_RUN: //** Find next non-wildcard character => treat run of */? as 1 * for (psz=psz2+1; (*psz == chWILD_RUN) || (*psz == chWILD_CHAR); CharIncr(psz)) { ; //** Skip through pattern string } //** *psz is either EOL, or not a wildcard chNext = *psz; // Character to terminate run //** Span until run terminates -- either while ((*psz1 != '\0') && // Don't run off filespec (*psz1 != chNext) && // Stop at run terminator (*psz1 != chNAME_EXT_SEP)) { // "." not allowed to match CharIncr(psz1); } //** At this point, we've matched as much as we could; // If there is a failure, the next iteration through the // loop will find it; So, just update the pattern position. psz2 = psz; break; case chWILD_CHAR: if (*psz1 == chNAME_EXT_SEP) { // Match anything but "." return FALSE; // Found point of mismatch } if (*psz1) CharIncr(psz1); // Next position in filespec CharIncr(psz2); // Next position in pattern break; case chNAME_EXT_SEP: if (*psz1 == chNAME_EXT_SEP) { psz1++; CharIncr(psz2); } else if (*psz1 == '\0') { CharIncr(psz2); } else { return FALSE; } break; default: if (toupper(*psz1) != toupper(*psz2)) { // Still match return FALSE; // Found point of mismatch } if (*psz1) CharIncr(psz1); // Next position in filespec CharIncr(psz2); // Next position in pattern break; } } //** We have a match if *both* strings were fully consumed return ((*psz1 == '\0') && (*psz2 == '\0')); } /* IsWildMatch() */ #pragma optimize("",off) // Avoid optimizer warning on in-line ASM /*** IsPathRemovable - See if path refers to a removable media drive * * NOTE: See fileutil.h for entry/exit conditions. */ BOOL IsPathRemovable(char *pszPath, char *pchDrive) { char ach[4]="x:\\"; // Buffer for "x:\" BOOL fRemovable; char iDrive; //** Get drive for path if ((strlen(pszPath) >= 2) && isalpha(pszPath[0]) && (pszPath[1] == chDRIVE_SEP)) { iDrive = toupper(pszPath[0]) - 'A' + 1; } else { iDrive = (char)_getdrive(); } *pchDrive = 'A' + iDrive - 1; // Return drive letter #ifdef BIT16 //** Do it the MS-DOS way _asm { mov fRemovable,0 ; Assume not removable mov bl,iDrive ; (0=default; 1=A, ...) mov ax,4408h ; IOCTL Get Removable Media int 21h ; Call MS-DOS jc not_removable ; Error, assume not removable or ax,ax ; Test removability flag jne not_removable mov fRemovable,1 ; Drive is removable not_removable: } #else // !BIT16 //** Do it the Win32 way ach[0] = *pchDrive; // Construct path to root of drive to test fRemovable = GetDriveType(ach) == DRIVE_REMOVABLE; #endif return fRemovable; // Return removability } /* IsPathRemovable() */ #pragma optimize("",on) // Restore previous Optimization settings /*** GetFileTimeAndAttr - Get date, time, and attributes from a file * * NOTE: See fileutil.h for entry/exit conditions. */ BOOL GetFileTimeAndAttr(PFILETIMEATTR pfta, char *pszFile, PERROR perr) { #ifdef BIT16 //** Do it the MS-DOS way int hf; hf = _open(pszFile, _O_RDONLY | _O_BINARY); if (hf == -1) { ErrSet(perr,pszFILERR_OPEN_FAILED,"%s",pszFile); return FALSE; } //WARNING: 30-Mar-1994 bens Ignore errors??? _dos_getftime(hf,&pfta->date,&pfta->time); _close(hf); _dos_getfileattr(pszFile,&pfta->attr); return TRUE; #else // !BIT16 //** Do it the Win32 way BOOL rc; FILETIME ft; FILETIME ftUTC; // Win32 returns Universal Time Code HANDLE hfQuery; hfQuery = CreateFile(pszFile, // open again with Win32 GENERIC_READ, // Just to read FILE_SHARE_READ,// Coexist with previous open NULL, // No security OPEN_EXISTING, // Must exist 0L, // We're not setting any attributes NULL); // No template handle if (hfQuery == INVALID_HANDLE_VALUE) { ErrSet(perr,pszFILERR_OPEN_FAILED,"%s",pszFile); return FALSE; } //** Get date/time and convert it rc = GetFileTime(hfQuery,NULL,NULL,&ftUTC); rc |= FileTimeToLocalFileTime(&ftUTC,&ft); // Apply timezone rc |= FileTimeToDosDateTime(&ft,&pfta->date,&pfta->time); CloseHandle(hfQuery); //** Get attributes and convert them pfta->attr = AttrFATFromAttr32(GetFileAttributes(pszFile)); if (!rc) { ErrSet(perr,pszFILERR_CANNOT_GET_FILE_INFO,"%s",pszFile); return FALSE; } return TRUE; #endif } /* GetFileTimeAndAttr() */ /*** SetFileTimeAndAttr - Set date, time, and attributes of a file * * NOTE: See fileutil.h for entry/exit conditions. */ BOOL SetFileTimeAndAttr(char *pszFile, PFILETIMEATTR pfta, PERROR perr) { #ifdef BIT16 //** Do it the MS-DOS way int hf; hf = _open(pszFile,_O_WRONLY | _O_BINARY); if (hf == -1) { ErrSet(perr,pszFILERR_OPEN_FAILED,"%s",pszFile); return FALSE; } _dos_setftime(hf,pfta->date,pfta->time); _close(hf); _dos_setfileattr(pszFile,pfta->attr); return TRUE; #else // !BIT16 //** Do it the Win32 way HANDLE hfSet; FILETIME ft; FILETIME ftUTC; // Win32 needs Universal Time Code BOOL rc; hfSet = CreateFile(pszFile, // open with Win32 GENERIC_WRITE, // Need to be able to modify properties 0, // Deny all NULL, // No security OPEN_EXISTING, // Must exist 0L, // We're not setting any attributes NULL); // No template handle if (hfSet == INVALID_HANDLE_VALUE) { // Changed this to retry because NT 4.0 occasionally returns a // sharing violation even though we've just closed the file. // Although this always worked in testing, also made it non-fatal so // that if the retry doesn't work it won't abort the extraction. Sleep(100); // give OS opportunity to sort it out hfSet = CreateFile(pszFile, // open with Win32 GENERIC_WRITE, // Need to be able to modify properties 0, // Deny all NULL, // No security OPEN_EXISTING, // Must exist 0L, // We're not setting any attributes NULL); // No template handle if (hfSet == INVALID_HANDLE_VALUE) { return TRUE; } } rc = DosDateTimeToFileTime(pfta->date,pfta->time,&ft); rc |= LocalFileTimeToFileTime(&ft, &ftUTC); // Apply timezone rc |= SetFileTime(hfSet,NULL,NULL,&ftUTC); CloseHandle(hfSet); rc |= SetFileAttributes(pszFile,Attr32FromAttrFAT(pfta->attr)); if (!rc) { ErrSet(perr,pszFILERR_CANNOT_SET_FILE_INFO,"%s",pszFile); return FALSE; } return TRUE; #endif // !BIT16 } /* SetFileTimeAndAttr() */ /*** CopyOneFile - Make a faithful copy of a file * * NOTE: See fileutil.h for entry/exit conditions. */ BOOL CopyOneFile(char *pszDst, char *pszSrc, BOOL fCopy, UINT cbBuffer, PFNOVERRIDEFILEPROPERTIES pfnofp, void *pv, PERROR perr) { UINT cbRead; UINT cbWritten; BOOL fSuccess = FALSE; // Assume failure FILETIMEATTR fta; int hfDst = -1; int hfSrc = -1; char *pbuf = NULL; //** Open source hfSrc = _open(pszSrc, _O_RDONLY | _O_BINARY); if (hfSrc == -1) { ErrSet(perr,pszFILERR_OPEN_FAILED,"%s",pszSrc); goto cleanup; } //** Get file date, time, and attributes for source file if (!GetFileTimeAndAttr(&fta,pszSrc,perr)) { goto cleanup; } //** Permit caller to override date/time/attr if (pfnofp != NULL) { if (!(*pfnofp)(&fta,pv,perr)) { // Call override function goto cleanup; // Error, go cleanup } } //** Early out if we were just merging file date/time/attr values if (!fCopy) { fSuccess = TRUE; // Success goto cleanup; // Go close source and exit } //** Get copy buffer if (!(pbuf = MemAlloc(cbBuffer))) { ErrSet(perr,pszFILERR_NO_MEMORY_FOR_BUFFER,"%s%s",pszSrc,pszDst); goto cleanup; } //** Open destination hfDst = _open(pszDst, _O_BINARY | _O_WRONLY | _O_CREAT | _O_TRUNC, // No translation, R/W _S_IREAD | _S_IWRITE); // Attributes when file is closed if (hfDst == -1) { ErrSet(perr,pszFILERR_OPEN_FAILED,"%s",pszDst); goto cleanup; } //** Copy data while (!_eof(hfSrc)) { //** Read chunk cbRead = _read(hfSrc,pbuf,cbBuffer); if (cbRead == -1) { ErrSet(perr,pszFILERR_READ_FILE,"%s",pszSrc); goto cleanup; } else if (cbRead != 0) { // Not at EOF //** Write it cbWritten = _write(hfDst,pbuf,cbRead); if (cbWritten != cbRead) { ErrSet(perr,pszFILERR_WRITE_FILE,"%s",pszSrc); goto cleanup; } } } //** Done copying, close destination file handle _close(hfDst); hfDst = -1; // Avoid unnecessary close in cleanup //** Set file date, time, and attributes if (!SetFileTimeAndAttr(pszDst,&fta,perr)) { goto cleanup; } //** Success! fSuccess = TRUE; cleanup: if (hfDst != -1) { _close(hfDst); } if (hfSrc != -1) { _close(hfSrc); } if (pbuf) { MemFree(pbuf); } return fSuccess; } /* CopyOneFile() */ #ifndef BIT16 //** Win32 stuff /*** Attr32FromAttrFAT - Convert FAT file attributes to Win32 form * * NOTE: See fileutil.h for entry/exit conditions. */ DWORD Attr32FromAttrFAT(WORD attrMSDOS) { //** Quick out for normal file special case if (attrMSDOS == _A_NORMAL) { return FILE_ATTRIBUTE_NORMAL; } //** Otherwise, mask off read-only, hidden, system, and archive bits // NOTE: These bits are in the same places in MS-DOS and Win32! // Assert(_A_RDONLY == FILE_ATTRIBUTE_READONLY); Assert(_A_HIDDEN == FILE_ATTRIBUTE_HIDDEN); Assert(_A_SYSTEM == FILE_ATTRIBUTE_SYSTEM); Assert(_A_ARCH == FILE_ATTRIBUTE_ARCHIVE); return attrMSDOS & (_A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_ARCH); } /*** AttrFATFromAttr32 - Convert Win32 file attributes to FAT form * * NOTE: See fileutil.h for entry/exit conditions. */ WORD AttrFATFromAttr32(DWORD attr32) { //** Quick out for normal file special case if (attr32 & FILE_ATTRIBUTE_NORMAL) { return _A_NORMAL; } //** Otherwise, mask off read-only, hidden, system, and archive bits // NOTE: These bits are in the same places in MS-DOS and Win32! // Assert(_A_RDONLY == FILE_ATTRIBUTE_READONLY); Assert(_A_HIDDEN == FILE_ATTRIBUTE_HIDDEN); Assert(_A_SYSTEM == FILE_ATTRIBUTE_SYSTEM); Assert(_A_ARCH == FILE_ATTRIBUTE_ARCHIVE); return ((WORD)attr32) & (_A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_ARCH); } #endif // !BIT16