//-------------------------------------------------------------- // // File: loadfiles // // Contents: Load files. // //--------------------------------------------------------------- #include "loadhead.cxx" #pragma hdrstop #include #include #include #include #include #include #define CREATE_DIRECTORY_MAX_PATH 248 //--------------------------------------------------------------- CSection *g_pcsSectionList = NULL; CSpecialDirectory g_csdCurrent; CSpecialDirectory g_csdOld; CRuleList g_crlExcludeWildcards; CRuleList g_crlIncludeWildcards; //--------------------------------------------------------------- DWORD ComputeTemp() { return ERROR_SUCCESS; } //--------------------------------------------------------------- void EraseTemp() { } DWORD FixShellShortcut(const TCHAR *ptsFile) { HRESULT hr; IShellLink *pisl = NULL; IPersistFile *pipf = NULL; TCHAR *ptsDest = NULL; WIN32_FIND_DATA wfd; if (DebugOutput) Win32Printf(LogFile, "Called FixShellShortcut for %s\r\n", ptsFile); hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&pisl); if (FAILED(hr)) goto cleanup; hr = pisl->QueryInterface(IID_IPersistFile, (void **)&pipf); if (FAILED(hr)) goto cleanup; hr = pipf->Load(ptsFile, 0); if (FAILED(hr)) goto cleanup; TCHAR ptsPath[MAX_PATH + 1]; hr = pisl->GetPath(ptsPath, MAX_PATH + 1, &wfd, SLGP_RAWPATH); if (FAILED(hr)) goto cleanup; if (DebugOutput) Win32Printf(LogFile, "Retrieved path %s from shell link %s\r\n", ptsPath, ptsFile); hr = WhereIsThisFile(ptsPath, &ptsDest); if (hr == ERROR_SUCCESS) { //Change the shortcut //Expand any environment strings in the original data with //the values for the user we're loading for. If the final //paths match, we'll retain the version of the data with the //unexpanded environment variables in it. Otherwise, we take //the full path. //We could be much smarter here if we wanted to. //For instance: // 1) Check the old path against the old special directories // list, and try to remap things that match. // 2) Double check to make sure the shortcut is actually // broken before we go ahead and fix it. TCHAR tsExpand[MAX_PATH + 1]; TCHAR tsTemp[MAX_PATH + 1]; TCHAR *ptsFinalDest; if (_tcslen(ptsPath) > MAX_PATH) { if (DebugOutput) Win32Printf(LogFile, "Error: ptsPath too long %s\r\n", ptsPath); goto cleanup; } _tcscpy(tsExpand, ptsPath); hr = ExpandEnvStringForUser(tsExpand, tsTemp, &ptsFinalDest); if (hr) goto cleanup; if (_tcsicmp(ptsDest, ptsFinalDest) == 0) { //They're the same, use the string with the environment //variables in it. ptsFinalDest = ptsPath; } else { ptsFinalDest = ptsDest; } hr = pisl->SetPath(ptsFinalDest); if (FAILED(hr)) goto cleanup; if (DebugOutput) Win32Printf(LogFile, "FixShellShortcut fixed %s from %s to %s\r\n", ptsFile, ptsPath, ptsFinalDest); } //If this function failed, we leave the shortcut alone. A possible //change is to delete the shortcut for this case. cleanup: if (ptsDest != NULL) free(ptsDest); if (pipf != NULL) pipf->Release(); if (pisl != NULL) pisl->Release(); return ERROR_SUCCESS; } DWORD ExpandEnvStringForUser(TCHAR *ptsString, TCHAR *ptsTemp, TCHAR **pptsFinal) { return g_csdCurrent.ExpandMacro(ptsString, ptsTemp, pptsFinal, FALSE); } //--------------------------------------------------------------- DWORD WhereIsThisFile( const TCHAR *ptsFile, TCHAR **pptsNewFile ) { CSection *pcsCurrent = g_pcsSectionList; TCHAR tsExpName[MAX_PATH + 1]; TCHAR tsTemp[MAX_PATH + 1]; TCHAR *ptsFullName; DWORD dwErr; if (_tcslen(ptsFile) > MAX_PATH) { if (DebugOutput) Win32Printf(LogFile, "Error: ptsFile too long %s\r\n", ptsFile); return ERROR_FILENAME_EXCED_RANGE; } _tcscpy(tsExpName, ptsFile); dwErr = g_csdOld.ExpandMacro(tsExpName, tsTemp, &ptsFullName, FALSE); if (dwErr) return dwErr; while (pcsCurrent != NULL) { TCHAR *ptsFileOnly; ptsFileOnly = _tcsrchr(ptsFullName, '\\'); if ((NULL == ptsFileOnly) || (_tcsnicmp(ptsFullName, pcsCurrent->GetSectionPath(), pcsCurrent->GetSectionPathLength()) == 0)) { //Section matches, search for file if (NULL == ptsFileOnly) { ptsFileOnly = ptsFullName; } else { ptsFileOnly = ptsFullName + pcsCurrent->GetSectionPathLength() + 1; } for (ULONG i = 0; i < pcsCurrent->GetNameCount(); i++) { const TCHAR *ptsCurrent = pcsCurrent->GetFullFileName(i); if (_tcsicmp(ptsFileOnly, ptsCurrent) == 0) { TCHAR tsDest[MAX_PATH + 1]; const TCHAR *ptsDest = pcsCurrent->GetDestination(i); if (ptsDest == NULL) { DWORD dwDestLen = pcsCurrent->GetSectionDestLength(); if (dwDestLen + _tcslen(ptsCurrent) + 1 > MAX_PATH) { if (Verbose) { Win32Printf(LogFile, "Error: destination too long %s\\%s\r\n", pcsCurrent->GetSectionDest(), ptsCurrent); } return ERROR_FILENAME_EXCED_RANGE; } _tcscpy(tsDest, pcsCurrent->GetSectionDest()); tsDest[dwDestLen] = TEXT('\\'); _tcscpy(tsDest + dwDestLen + 1, ptsCurrent); ptsDest = tsDest; } //Bingo, direct hit. *pptsNewFile = (TCHAR *) malloc( (_tcslen(ptsDest) + 1) * sizeof(TCHAR)); if (*pptsNewFile == NULL) return ERROR_NOT_ENOUGH_MEMORY; _tcscpy( *pptsNewFile, ptsDest); if (DebugOutput) Win32Printf(LogFile, "WhereIsThisFile(%ws) found %ws\r\n", ptsFile, *pptsNewFile); return ERROR_SUCCESS; } } } pcsCurrent = pcsCurrent->GetNextSection(); } return ERROR_NOT_FOUND; } DWORD AddInfSectionToRuleList(INFCONTEXT *pic, CRuleList *pfl, BOOL fAllowRename) { TCHAR buf[MAX_PATH + 1]; TCHAR bufMacro[MAX_PATH + 1]; TCHAR *ptsFinalName; TCHAR bufTag[MAX_PATH + 1]; DWORD dwErr; CRuleList *prl; do { DWORD dwIndex; DWORD cFields; BOOL fDirectoryTag = FALSE; cFields = SetupGetFieldCount(pic); if (((cFields != 1) && !fAllowRename) || ((cFields > 2) && fAllowRename)) { Win32PrintfResource(LogFile, IDS_INF_ERROR); if (Verbose) Win32Printf(STDERR, "Line contains more than one file name\r\n"); return ERROR_INVALID_PARAMETER; } if (!SetupGetStringField(pic, 1, buf, MAX_PATH + 1, NULL)) { dwErr = GetLastError(); Win32PrintfResource(LogFile, IDS_INF_ERROR); if (Verbose) Win32Printf(STDERR, "SetupGetStringField returned %lu\r\n", dwErr); return dwErr; } if (SetupGetStringField(pic, 0, bufTag, MAX_PATH + 1, NULL)) { if (_tcsicmp(bufTag, buf)) { //Someone put a field identifier on there. The only //one we recognize is 'dir' if (_tcsicmp(bufTag, TEXT("dir"))) { Win32PrintfResource(LogFile, IDS_INF_ERROR); if (Verbose) Win32Printf(STDERR, "Unknown tag %s\r\n", bufTag); return ERROR_INVALID_PARAMETER; } fDirectoryTag = TRUE; } } dwErr = g_csdOld.ExpandMacro(buf, bufMacro, &ptsFinalName, TRUE); if (dwErr) return dwErr; if (fDirectoryTag) { //Append a backslash if (_tcslen(ptsFinalName) >= MAX_PATH) { if (DebugOutput) { Win32Printf(LogFile, "Error: ptsFinalName too long %s\r\n", ptsFinalName); } return ERROR_FILENAME_EXCED_RANGE; } _tcscat(ptsFinalName, TEXT("\\")); } dwErr = pfl->SetName(ptsFinalName, &prl); if (dwErr) { return dwErr; } if (cFields == 2) { if (!SetupGetStringField(pic, 2, buf, MAX_PATH + 1, NULL)) { dwErr = GetLastError(); Win32PrintfResource(LogFile, IDS_INF_ERROR); if (Verbose) Win32Printf(STDERR, "SetupGetStringField returned %lu\r\n", dwErr); return dwErr; } dwErr = prl->SetDestination(buf); if (dwErr) { return dwErr; } } } while (SetupFindNextLine(pic, pic)); return ERROR_SUCCESS; } DWORD ProcessCopyFiles(HINF hi, const TCHAR *ptsName) { DWORD dwErr; INFCONTEXT ic; if (!SetupFindFirstLine(hi, ptsName, NULL, &ic)) { dwErr = GetLastError(); Win32PrintfResource(LogFile, IDS_SECTION_NAME_NOT_FOUND, ptsName); if (Verbose) Win32Printf(STDERR, "SetupFindFirstLine failed on section %s with %lu\r\n", ptsName, dwErr); return dwErr; } dwErr = AddInfSectionToRuleList(&ic, &g_crlIncludeWildcards, TRUE); return dwErr; } DWORD ProcessDelFiles(HINF hi, const TCHAR *ptsName) { DWORD dwErr; INFCONTEXT ic; if (!SetupFindFirstLine(hi, ptsName, NULL, &ic)) { dwErr = GetLastError(); Win32PrintfResource(LogFile, IDS_SECTION_NAME_NOT_FOUND, ptsName); if (Verbose) Win32Printf(STDERR, "SetupFindFirstLine failed on section %s with %lu\r\n", ptsName, dwErr); return dwErr; } dwErr = AddInfSectionToRuleList(&ic, &g_crlExcludeWildcards, FALSE); return dwErr; } DWORD ProcessRules(HINF hi) { DWORD dwErr; INFCONTEXT ic; if (!SetupFindFirstLine(hi, EXTENSION_SECTION, NULL, &ic)) { //Ignore - this section is optional return ERROR_SUCCESS; } do { DWORD cFields; cFields = SetupGetFieldCount(&ic); TCHAR buf[MAX_PATH + 1]; if (!SetupGetStringField(&ic, 0, buf, MAX_PATH + 1, NULL)) { dwErr = GetLastError(); Win32PrintfResource(LogFile, IDS_INF_ERROR); if (Verbose) Win32Printf(STDERR, "SetupGetStringField failed with %lu\r\n", dwErr); return dwErr; } if (_tcsicmp(buf, COPYFILES_LABEL) == 0) { //Add files in all sections to the include list for (DWORD j = 1; j < cFields + 1; j++) { if (!SetupGetStringField(&ic, j, buf, MAX_PATH + 1, NULL)) { dwErr = GetLastError(); Win32PrintfResource(LogFile, IDS_INF_ERROR); if (Verbose) Win32Printf(STDERR, "SetupGetStringField failed with %lu\r\n", dwErr); return dwErr; } dwErr = ProcessCopyFiles(hi, buf); if (dwErr != 0) return dwErr; } } else if (_tcsicmp(buf, DELFILES_LABEL) == 0) { //Add files in all sections to the include list for (DWORD j = 1; j < cFields + 1; j++) { if (!SetupGetStringField(&ic, j, buf, MAX_PATH + 1, NULL)) { dwErr = GetLastError(); Win32PrintfResource(LogFile, IDS_INF_ERROR); if (Verbose) Win32Printf(STDERR, "SetupGetStringField failed with %lu\r\n", dwErr); return dwErr; } dwErr = ProcessDelFiles(hi, buf); if (dwErr != 0) return dwErr; } } } while (SetupFindNextLine(&ic, &ic)); return ERROR_SUCCESS; } DWORD ProcessSpecialDirs(HINF hi) { DWORD dwErr; INFCONTEXT ic; if (!SetupFindFirstLine(hi, SPECIALDIRS_SECTION, NULL, &ic)) { dwErr = GetLastError(); Win32PrintfResource(LogFile, IDS_SECTION_NAME_NOT_FOUND, SPECIALDIRS_SECTION); if (Verbose) Win32Printf(STDERR, "SetupFindFirstLine failed with %lu\r\n", dwErr); return dwErr; } do { TCHAR bufName[MAX_PATH + 1]; TCHAR bufPath[MAX_PATH + 2]; DWORD cFields; cFields = SetupGetFieldCount(&ic); if (cFields != 1) { Win32PrintfResource(LogFile, IDS_INF_ERROR); if (Verbose) Win32Printf(STDERR, "INF line contains too many fields in " "section %s\r\n", SPECIALDIRS_SECTION); return ERROR_INVALID_PARAMETER; } if (!SetupGetStringField(&ic, 0, bufName, MAX_PATH + 1, NULL)) { dwErr = GetLastError(); Win32PrintfResource(LogFile, IDS_INF_ERROR); if (Verbose) Win32Printf(STDERR, "SetupGetStringField returned %lu\r\n", dwErr); return dwErr; } if (!SetupGetStringField(&ic, 1, bufPath, MAX_PATH + 1, NULL)) { dwErr = GetLastError(); Win32PrintfResource(LogFile, IDS_INF_ERROR); if (Verbose) Win32Printf(STDERR, "SetupGetStringField returned %lu\r\n", dwErr); return dwErr; } dwErr = g_csdOld.InitFromInf(bufName, bufPath); if (dwErr) return dwErr; } while (SetupFindNextLine(&ic, &ic)); return ERROR_SUCCESS; } DWORD AddLoadFileSection(HINF hi, TCHAR *ptsName, CSection **ppcs) { DWORD dwErr; INFCONTEXT ic; CSection *pcsSection; TCHAR tsSection[MAX_PATH + 1]; TCHAR buf[MAX_PATH + 1]; TCHAR bufDest[MAX_PATH + 1]; TCHAR bufMacro[MAX_PATH + 1]; TCHAR *ptsFinalName; pcsSection = new CSection; if (pcsSection == NULL) { Win32PrintfResource(LogFile, IDS_NOT_ENOUGH_MEMORY); return ERROR_OUTOFMEMORY; } if (ppcs) *ppcs = pcsSection; if (g_pcsSectionList == NULL) { g_pcsSectionList = pcsSection; } else { g_pcsSectionList->AddToList(pcsSection); } dwErr = pcsSection->SetSectionTitle(ptsName); if (dwErr) { return dwErr; } if (_tcslen(ptsName) > MAX_PATH) { if (DebugOutput) Win32Printf(LogFile, "Error: ptsName too long %s\r\n", ptsName); return ERROR_FILENAME_EXCED_RANGE; } _tcscpy(buf, ptsName); dwErr = g_csdOld.ExpandMacro(buf, bufMacro, &ptsFinalName, TRUE); if (dwErr) { //Try with the current list dwErr = g_csdCurrent.ExpandMacro(buf, bufMacro, &ptsFinalName, FALSE); if (dwErr) return dwErr; } dwErr = pcsSection->SetSectionPath(ptsFinalName); if (dwErr) { return dwErr; } const TCHAR *ptsSectionPath = pcsSection->GetSectionPath(); if (_tcslen(ptsSectionPath) + 1 > MAX_PATH) { if (DebugOutput) Win32Printf(LogFile, "Error: ptsSectionPath too long %s\r\n", ptsSectionPath); return ERROR_FILENAME_EXCED_RANGE; } _tcscpy(tsSection, ptsSectionPath); _tcscat(tsSection, TEXT("\\")); if (!SetupFindFirstLine(hi, ptsName, NULL, &ic)) { dwErr = GetLastError(); Win32PrintfResource(LogFile, IDS_SECTION_NAME_NOT_FOUND, ptsName); if (Verbose) Win32Printf(STDERR, "SetupFindFirstLine failed on section %s with %lu\r\n", ptsName, dwErr); return dwErr; } do { DWORD cFields; cFields = SetupGetFieldCount(&ic); if ((cFields != 1) && (cFields != 2)) { Win32PrintfResource(LogFile, IDS_INF_ERROR); if (Verbose) Win32Printf(STDERR, "INF line contains too many fields in " "section %s\r\n", ptsName); return ERROR_INVALID_PARAMETER; } if (!SetupGetStringField(&ic, 1, buf, MAX_PATH + 1, NULL)) { dwErr = GetLastError(); Win32PrintfResource(LogFile, IDS_INF_ERROR); if (Verbose) Win32Printf(STDERR, "SetupGetStringField returned %lu\r\n", dwErr); return dwErr; } dwErr = g_csdCurrent.ExpandMacro(buf, bufMacro, &ptsFinalName, TRUE); if (dwErr) return dwErr; //Check if we're supposed to exclude this file by a rule CRuleList *prl; if (!g_crlExcludeWildcards.MatchAgainstRuleList(tsSection, buf, &prl, NULL)) { DWORD i; dwErr = pcsSection->SetName(ptsFinalName, &i, FALSE); if (dwErr) return dwErr; if (cFields == 2) { if (!SetupGetStringField(&ic, 2, bufDest, MAX_PATH + 1, NULL)) { dwErr = GetLastError(); Win32PrintfResource(LogFile, IDS_INF_ERROR); if (Verbose) Win32Printf(STDERR, "SetupGetStringField returned %lu\r\n", dwErr); return dwErr; } dwErr = g_csdCurrent.ExpandMacro(bufDest, bufMacro, &ptsFinalName, TRUE); if (dwErr) return dwErr; dwErr = pcsSection->SetDestination(ptsFinalName, i); if (dwErr) return dwErr; } } else { if (Verbose) Win32Printf(LogFile, "Excluding %s by rule %s\r\n", buf, prl->GetFullName()); } } while (SetupFindNextLine(&ic, &ic)); return ERROR_SUCCESS; } DWORD CopyAllFiles(void) { DWORD dwErr; CSection *pcs = g_pcsSectionList; DWORD ccMigPath; TCHAR tsFinalSource[MAX_PATH + 1]; TCHAR tsSectionDest[MAX_PATH + 1]; TCHAR tsFinalDest[MAX_PATH + 1]; TCHAR *ptsJustFile; DWORD ccPredictedLength; DWORD dwReturnUp = ERROR_SUCCESS; #ifdef UNICODE if (_tcslen(wcsMigrationPath) > MAX_PATH) { if (DebugOutput) Win32Printf(LogFile, "Error: wcsMigrationPath too long %s\r\n", wcsMigrationPath); return ERROR_FILENAME_EXCED_RANGE; } wcscpy(tsFinalSource, wcsMigrationPath); ccMigPath = wcslen(wcsMigrationPath); #else if (_tcslen(MigrationPath) > MAX_PATH) { if (DebugOutput) Win32Printf(LogFile, "Error: MigrationPath too long %s\r\n", MigrationPath); return ERROR_FILENAME_EXCED_RANGE; } strcpy(tsFinalSource, MigrationPath); ccMigPath = strlen(MigrationPath); #endif while (pcs != NULL) { ULONG ulNameCount = pcs->GetNameCount(); //Add section to source path const TCHAR *ptsSection = pcs->GetSectionPath(); DWORD ccSection = pcs->GetSectionPathLength() + ccMigPath; if ( ccSection + 1 > MAX_PATH ) { Win32PrintfResource(LogFile, IDS_FILE_COPYERROR, ptsSection); if (Verbose) { Win32Printf(STDERR, "Skipping Too Long Source Filename: %s\\%s\r\n", tsFinalSource, ptsSection); } if (dwReturnUp == ERROR_SUCCESS) { dwReturnUp = ERROR_FILENAME_EXCED_RANGE; } pcs = pcs->GetNextSection(); continue; } tsFinalSource[ccMigPath] = TEXT('\\'); tsFinalSource[ccMigPath + 1] = ptsSection[0]; _tcscpy(tsFinalSource + ccMigPath + 2, ptsSection + 2); tsFinalSource[ccSection++] = TEXT('\\'); tsFinalSource[ccSection] = 0; // Build Destination Path INT_PTR ccDest = pcs->GetSectionDestLength(); if ( ccDest + 1 > MAX_PATH ) { Win32PrintfResource(LogFile, IDS_FILE_COPYERROR, pcs->GetSectionDest); if (Verbose) { Win32Printf(STDERR, "Skipping Too Long Destination Filename: %s\r\n", pcs->GetSectionDest); } if (dwReturnUp == ERROR_SUCCESS) { dwReturnUp = ERROR_FILENAME_EXCED_RANGE; } pcs = pcs->GetNextSection(); continue; } _tcscpy(tsSectionDest, pcs->GetSectionDest()); if (tsSectionDest[ccDest - 1] != TEXT('\\')) { tsSectionDest[ccDest++] = TEXT('\\'); tsSectionDest[ccDest] = 0; } for (ULONG i = 0; i < ulNameCount; i++) { TCHAR *ptsDestFinal; const TCHAR *ptsName = pcs->GetFullFileName(i); const TCHAR *ptsDest = pcs->GetDestination(i); if (ptsDest != NULL) { TCHAR *ptsTopDir; // File is explicitly being migrated // Build the destination filename with these pieces: // - Destination dir specified in the file rule // - Last piece of the path in the section heading // - Filename DWORD ccFinalDest = _tcslen(ptsDest); if (ccFinalDest > MAX_PATH) { Win32PrintfResource(LogFile, IDS_FILE_COPYERROR, ptsName); if (Verbose) Win32Printf(STDERR, "Skipping Too Long Destination Filename: %s\r\n", ptsDest); continue; } _tcscpy(tsFinalDest, ptsDest); if (tsFinalDest[ccFinalDest] != TEXT('\\') && ccFinalDest < MAX_PATH) { tsFinalDest[ccFinalDest++] = TEXT('\\'); } ptsTopDir = _tcsrchr( ptsSection, TEXT('\\')); if (ptsTopDir != NULL) { ptsTopDir++; // move past the '\' } // Skip this if we're going to create a filename that is too long ccPredictedLength = ccFinalDest + _tcslen(ptsName); if (ptsTopDir != NULL) { ccPredictedLength += _tcslen(ptsTopDir) + 1; } if ( ccPredictedLength > MAX_PATH ) { Win32PrintfResource(LogFile, IDS_FILE_COPYERROR, ptsName); if (Verbose) { tsFinalDest[ccFinalDest] = 0; // Null terminate for printing if (ptsTopDir == NULL) Win32Printf(STDERR, "Skipping Too Long Destination Filename: %s%s\r\n", tsFinalDest, ptsName); else Win32Printf(STDERR, "Skipping Too Long Destination Filename: %s%s\\%s\r\n", tsFinalDest, ptsTopDir, ptsName); } if (dwReturnUp == ERROR_SUCCESS) { dwReturnUp = ERROR_FILENAME_EXCED_RANGE; } continue; } if (ptsTopDir != NULL) { _tcscpy(tsFinalDest + ccFinalDest, ptsTopDir); ccFinalDest += _tcslen(ptsTopDir); if (tsFinalDest[ccFinalDest] != TEXT('\\') ) { tsFinalDest[ccFinalDest++] = TEXT('\\'); } } _tcscpy(tsFinalDest + ccFinalDest, ptsName); ptsDestFinal = tsFinalDest; } else { if ( ccDest + _tcslen(ptsName) > MAX_PATH ) { Win32PrintfResource(LogFile, IDS_FILE_COPYERROR, ptsName); if (Verbose) { tsFinalDest[ccDest] = 0; // Null terminate for printing Win32Printf(STDERR, "Skipping Too Long Destination Filename: %s%s\r\n", tsFinalDest, ptsName); } if (dwReturnUp == ERROR_SUCCESS) { dwReturnUp = ERROR_FILENAME_EXCED_RANGE; } continue; } //Use section destination _tcscpy(tsSectionDest + ccDest, ptsName); ptsDestFinal = tsSectionDest; } DWORD_PTR ccDestDir; // If the directory is more than 248 characters, then CreateDirectory will fail // There is no system define for 248, unfortunately.. It's only mentioned in the // documentation. How odd. ptsJustFile = _tcsrchr(ptsDestFinal, '\\'); if ( ptsJustFile == NULL ) { ccDestDir = _tcslen(ptsDestFinal); } else { ccDestDir = ptsJustFile - ptsDestFinal; ptsJustFile++; // Move past the '\' } if ( ccDestDir > CREATE_DIRECTORY_MAX_PATH ) { Win32PrintfResource(LogFile, IDS_FILE_COPYERROR, ptsName); if (Verbose) { ptsDestFinal[ccDestDir] = 0; // Null terminate for printing Win32Printf(STDERR, "Skipping Too Long Destination Directory: %s for %s\r\n", ptsDestFinal, ptsJustFile ? ptsJustFile : TEXT("file list") ); } if (dwReturnUp == ERROR_SUCCESS) { dwReturnUp = ERROR_FILENAME_EXCED_RANGE; } continue; } // Windows appears to enforce that an existing file cannot exceed MAX_PATH, // but we'll check just to make sure. if ( (ccSection + _tcslen(ptsName)) > MAX_PATH ) { Win32PrintfResource(LogFile, IDS_FILE_COPYERROR, ptsName); if (Verbose) { tsFinalSource[ccSection] = 0; // Null terminate for printing Win32Printf(STDERR, "Skipping Too Long Source Filename: %s\\%s\r\n", tsFinalSource, ptsName); } if (dwReturnUp == ERROR_SUCCESS) { dwReturnUp = ERROR_FILENAME_EXCED_RANGE; } continue; } _tcscpy(tsFinalSource + ccSection, ptsName); // Store the full destination in the file list pcs->SetDestination(ptsDestFinal, i); //Finally we have the filenames constructed, now try //the CopyFile operation BOOL fPath = FALSE; ULONG ulVersion = 1; TCHAR tsCollision[MAX_PATH + 1]; INT_PTR ccExt; TCHAR *ptsDestOriginal = ptsDestFinal; if (DebugOutput) { Win32Printf(LogFile, "Copying %s to %s\r\n", tsFinalSource, ptsDestFinal); } while (!CopyFile(tsFinalSource, ptsDestFinal, TRUE)) { dwErr = GetLastError(); if (dwErr == ERROR_PATH_NOT_FOUND) { if (fPath) { //We already tried to create the path, so something //else must be wrong. Punt-arooney. break; } dwErr = ERROR_SUCCESS; TCHAR *ptsPos; DWORD dwPos; //Try to create all the necessary directories TCHAR ptsDirectory[MAX_PATH + 1]; // ptsDestFinal was built inside this function and verified to // be less than MAX_PATH in length _tcscpy(ptsDirectory, ptsDestFinal); dwPos = 0; // Skip any leading drive specifier. if (ptsDirectory[0] == TEXT('\\')) dwPos = 1; else if (ptsDirectory[0] != 0 && ptsDirectory[1] == TEXT(':')) if (ptsDirectory[2] == TEXT('\\')) dwPos = 3; else dwPos = 2; //Create every directory along this path while (ptsPos = _tcschr(ptsDirectory + dwPos, TEXT('\\'))) { *ptsPos = 0; //Create the directory if (!CreateDirectory(ptsDirectory, NULL)) { dwErr = GetLastError(); if (dwErr != ERROR_ALREADY_EXISTS) { break; } dwErr = ERROR_SUCCESS; } //Put the backslash back in *ptsPos = TEXT('\\'); //Update dwLen dwPos = ptsPos - ptsDirectory + 1; } if (dwErr) break; fPath = TRUE; } else if (dwErr == ERROR_FILE_EXISTS) { TCHAR tsSquigs[MAX_PATH]; TCHAR *ptsDestSquig; INT_PTR ccSquig; //Add squiggles until we get an OK name. if (ptsDestFinal != tsCollision) { TCHAR *ptsDestExt; //First time ptsDestExt = _tcsrchr(ptsDestOriginal, TEXT('.')); if (ptsDestExt == NULL) { //No extension, just tack onto the end. ccExt = _tcslen(ptsDestOriginal); } else { ccExt = ptsDestExt - ptsDestOriginal; } // ptsDestOriginal was built inside this function and verified to // be less than MAX_PATH in length _tcscpy(tsCollision, ptsDestOriginal); ptsDestFinal = tsCollision; // temporarily terminate the original to find the squig. if( ptsDestExt != NULL ) *ptsDestExt = TEXT('\0'); ptsDestSquig = _tcsrchr(ptsDestOriginal, TEXT('(')); if( ptsDestSquig == NULL ) { ccSquig = ccExt; } else { ccSquig = ptsDestSquig - ptsDestOriginal; } // put the period back where we took it off. if( ptsDestExt != NULL ) *ptsDestExt = TEXT('.'); } wsprintf(tsSquigs, TEXT("(%lu)"), ulVersion++); if (_tcslen(ptsDestOriginal) + _tcslen(tsSquigs) > MAX_PATH) { Win32PrintfResource(LogFile, IDS_FILE_COPYERROR, tsFinalSource); if (Verbose) { Win32Printf(STDERR, "Could Not Copy To Too Long Destination Filename %s\r\n", ptsDestOriginal); } if (dwReturnUp == ERROR_SUCCESS) { dwReturnUp = ERROR_FILENAME_EXCED_RANGE; } continue; } wsprintf(tsCollision + ccSquig, TEXT("%s%s"), tsSquigs, ptsDestOriginal + ccExt); //Go back around and try again. } else { Win32PrintfResource(LogFile, IDS_FILE_COPYERROR, tsFinalSource); if (Verbose) { Win32Printf(STDERR, "Error %lu trying to copy %s to %s\r\n", dwErr, tsFinalSource, ptsDestFinal); } dwReturnUp = dwErr; break; } } if (ptsDestFinal == tsCollision) { dwErr = pcs->SetDestination(tsCollision, i); Win32Printf(LogFile, "Filename collision on %s, file renamed to %s\r\n", ptsDestOriginal, ptsDestFinal); } //Check if the file has a .lnk extension. TCHAR *ptsLastDot; ptsLastDot = _tcsrchr(ptsDestFinal, TEXT('.')); if (ptsLastDot != NULL) { if (_tcsicmp(ptsLastDot + 1, TEXT("lnk")) == 0) { //It's a link, try to fix it. Ignore errors. FixShellShortcut(ptsDestFinal); } } } pcs = pcs->GetNextSection(); } return dwReturnUp; } DWORD ParseInputFile(HINF hi) { DWORD dwErr; CSection *pcs; BOOL fMapping = FALSE; INFCONTEXT ic; INFCONTEXT icDestinationDirs; INFCONTEXT icDirectoryMapping; TCHAR tsFirstDestKey[MAX_PATH + 1]; TCHAR tsFirstMapping[10]; dwErr = ProcessSpecialDirs(hi); if (dwErr) return dwErr; dwErr = ProcessRules(hi); if (dwErr) return dwErr; if (!SetupFindFirstLine(hi, COPYFILE_SECTION, NULL, &ic)) { dwErr = GetLastError(); if (dwErr == ERROR_LINE_NOT_FOUND) { if (Verbose) Win32Printf(LogFile, "Warning: No [Copy These Files] section found.\r\n"); return ERROR_SUCCESS; } Win32PrintfResource(LogFile, IDS_SECTION_NAME_NOT_FOUND, COPYFILE_SECTION); if (Verbose) Win32Printf(STDERR, "SetupFindFirstLine failed with %lu\r\n", dwErr); return dwErr; } if (!SetupFindFirstLine(hi, DESTINATIONDIRS_SECTION, NULL, &icDestinationDirs)) { dwErr = GetLastError(); Win32PrintfResource(LogFile, IDS_SECTION_NAME_NOT_FOUND, DESTINATIONDIRS_SECTION); if (Verbose) Win32Printf(STDERR, "SetupFindFirstLine failed with %lu\r\n", dwErr); return dwErr; } //Get the key for the first line, we'll need it later. if (!SetupGetStringField(&icDestinationDirs, 0, tsFirstDestKey, MAX_PATH + 1, NULL)) { //Error dwErr = GetLastError(); Win32PrintfResource(LogFile, IDS_INF_ERROR); if (Verbose) Win32Printf(STDERR, "SetupGetStringField couldn't get " "first line in %s\r\n", DESTINATIONDIRS_SECTION); return dwErr; } if (SetupFindFirstLine(hi, DIRECTORYMAPPING_SECTION, NULL, &icDirectoryMapping)) { fMapping = TRUE; //Get the first key, we'll need it later. if (!SetupGetStringField(&icDirectoryMapping, 0, tsFirstMapping, 10, NULL)) { //Error dwErr = GetLastError(); Win32PrintfResource(LogFile, IDS_INF_ERROR); if (Verbose) Win32Printf(STDERR, "SetupGetStringField couldn't get " "first line in %s, error %lX\r\n", DIRECTORYMAPPING_SECTION, dwErr); return dwErr; } } else { //Ignore errors here, this section is optional } do { DWORD cFields; cFields = SetupGetFieldCount(&ic); TCHAR buf[MAX_PATH + 1]; if (cFields != 1) { Win32PrintfResource(LogFile, IDS_INF_ERROR); return ERROR_INVALID_PARAMETER; } if (!SetupGetStringField(&ic, 0, buf, MAX_PATH + 1, NULL)) { dwErr = GetLastError(); Win32PrintfResource(LogFile, IDS_INF_ERROR); if (Verbose) Win32Printf(STDERR, "SetupGetStringField failed with %lu\r\n", dwErr); return dwErr; } dwErr = AddLoadFileSection(hi, buf, &pcs); if (dwErr) return dwErr; if (fMapping) { INFCONTEXT icMap; TCHAR tsMapping[MAX_PATH + 1]; //Find the destination path for this directory. //First we have to check the first line, because // of SetupFindNextMatchLine if (_tcscmp(tsFirstDestKey, pcs->GetSectionTitle()) == 0) { //It's the first line icMap = icDestinationDirs; } else if (!SetupFindNextMatchLine(&icDestinationDirs, pcs->GetSectionTitle(), &icMap)) { //This is an error - we should have output this line //ourselves in scanstate. dwErr = GetLastError(); Win32PrintfResource(LogFile, IDS_INF_ERROR); if (Verbose) Win32Printf(STDERR, "SetupFindNextMatch couldn't find " "destination dir for %s\r\n", pcs->GetSectionTitle()); return dwErr; } if (!SetupGetStringField(&icMap, 1, tsMapping, MAX_PATH + 1, NULL)) { //Error, malformed INF dwErr = GetLastError(); Win32PrintfResource(LogFile, IDS_INF_ERROR); if (Verbose) Win32Printf(STDERR, "SetupGetIntField couldn't get " "destination dir for %s\r\n", pcs->GetSectionTitle()); return dwErr; } //Now look this up in the DirectoryMapping section if (_tcscmp(tsFirstMapping, tsMapping) == 0) icMap = icDirectoryMapping; if ((_tcscmp(tsFirstMapping, tsMapping) == 0) || (SetupFindNextMatchLine(&icDirectoryMapping, tsMapping, &icMap))) { TCHAR bufDest[MAX_PATH + 1]; if (!SetupGetStringField(&icMap, 1, bufDest, MAX_PATH + 1, NULL)) { //Error dwErr = GetLastError(); Win32PrintfResource(LogFile, IDS_INF_ERROR); if (Verbose) Win32Printf(STDERR, "SetupGetStringField couldn't get " "directory mapping for %s\r\n", tsMapping); return dwErr; } dwErr = pcs->SetSectionDest(bufDest); if (dwErr) { return dwErr; } } else { //Fall through } } if ((pcs->GetSectionDest())[0] == 0) { //Check for implicit relocation, for special directories TCHAR bufMacro[MAX_PATH + 1]; TCHAR *ptsFinal; const TCHAR *ptsSectionTitle = pcs->GetSectionTitle(); if (_tcslen(ptsSectionTitle) > MAX_PATH) { if (DebugOutput) Win32Printf(LogFile, "Error: ptsSectionTitle too long %s\r\n", ptsSectionTitle); return ERROR_FILENAME_EXCED_RANGE; } _tcscpy(buf, ptsSectionTitle); dwErr = g_csdCurrent.ExpandMacro(buf, bufMacro, &ptsFinal, TRUE); if (dwErr) return dwErr; //Compare to section path - if they're different, set the //section destination to the new section if (_tcsicmp(ptsFinal, pcs->GetSectionPath()) != 0) { dwErr = pcs->SetSectionDest(ptsFinal); if (dwErr) { return dwErr; } } } if ((pcs->GetSectionDest())[0] == 0) { dwErr = pcs->SetSectionDest(pcs->GetSectionPath()); if (dwErr) { return dwErr; } } } while (SetupFindNextLine(&ic, &ic)); return ERROR_SUCCESS; } //--------------------------------------------------------------- DWORD LoadFiles() { DWORD dwErr = ERROR_SUCCESS; if (UserPath == NULL) { UserPath = (TCHAR *) malloc(MAX_PATH + 1); if (UserPath == NULL) { Win32PrintfResource(LogFile, IDS_NOT_ENOUGH_MEMORY); dwErr = ERROR_OUTOFMEMORY; return dwErr; } //We need this but it hasn't been set in LoadUser, so get the //USERPROFILE variable for the current user and put it in there. dwErr = GetEnvironmentVariable(TEXT("USERPROFILE"), UserPath, MAX_PATH + 1); if (dwErr == 0) { //Fatal error. Win32PrintfResource(LogFile, IDS_NOT_ENOUGH_MEMORY); dwErr = ERROR_OUTOFMEMORY; return dwErr; } } dwErr = g_csdCurrent.InitForUser(CurrentUser); if (dwErr) return dwErr; if (DebugOutput) { for (ULONG i = 0; i < g_csdCurrent.GetDirectoryCount(); i++) { if (g_csdCurrent.GetDirectoryPath(i) != NULL) { Win32Printf(LogFile, "%s=%s\r\n", g_csdCurrent.GetDirectoryName(i), g_csdCurrent.GetDirectoryPath(i)); } } Win32Printf(LogFile, "\r\n"); } dwErr = ParseInputFile(InputInf); if (dwErr) return dwErr; //If CopyFiles is FALSE, do nothing if (!CopyFiles) return ERROR_SUCCESS; dwErr = CopyAllFiles(); if (dwErr) return dwErr; return ERROR_SUCCESS; }