#include "rar.hpp" // If NewFile==NULL, we delete created file after user confirmation. // It is useful we we need to overwrite an existing folder or file, // but need user confirmation for that. bool FileCreate(RAROptions *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize, bool *UserReject,int64 FileSize,RarTime *FileTime,bool WriteOnly) { if (UserReject!=NULL) *UserReject=false; #ifdef _WIN_ALL bool ShortNameChanged=false; #endif while (FileExist(Name)) { #if defined(_WIN_ALL) if (!ShortNameChanged) { // Avoid the infinite loop if UpdateExistingShortName returns // the same name. ShortNameChanged=true; // Maybe our long name matches the short name of existing file. // Let's check if we can change the short name. if (UpdateExistingShortName(Name)) continue; } // Allow short name check again. It is necessary, because rename and // autorename below can change the name, so we need to check it again. ShortNameChanged=false; #endif UIASKREP_RESULT Choice=uiAskReplaceEx(Cmd,Name,MaxNameSize,FileSize,FileTime,(NewFile==NULL ? UIASKREP_F_NORENAME:0)); if (Choice==UIASKREP_R_REPLACE) break; if (Choice==UIASKREP_R_SKIP) { if (UserReject!=NULL) *UserReject=true; return false; } if (Choice==UIASKREP_R_CANCEL) ErrHandler.Exit(RARX_USERBREAK); } // Try to truncate the existing file first instead of delete, // so we preserve existing file permissions such as NTFS permissions. uint FileMode=WriteOnly ? FMF_WRITE|FMF_SHAREREAD:FMF_UPDATE|FMF_SHAREREAD; if (NewFile!=NULL && NewFile->Create(Name,FileMode)) return true; CreatePath(Name,true); return NewFile!=NULL ? NewFile->Create(Name,FileMode):DelFile(Name); } bool GetAutoRenamedName(wchar *Name,size_t MaxNameSize) { wchar NewName[NM]; size_t NameLength=wcslen(Name); wchar *Ext=GetExt(Name); if (Ext==NULL) Ext=Name+NameLength; for (uint FileVer=1;;FileVer++) { swprintf(NewName,ASIZE(NewName),L"%.*ls(%u)%ls",uint(Ext-Name),Name,FileVer,Ext); if (!FileExist(NewName)) { wcsncpyz(Name,NewName,MaxNameSize); break; } if (FileVer>=1000000) return false; } return true; } #if defined(_WIN_ALL) // If we find a file, which short name is equal to 'Name', we try to change // its short name, while preserving the long name. It helps when unpacking // an archived file, which long name is equal to short name of already // existing file. Otherwise we would overwrite the already existing file, // even though its long name does not match the name of unpacking file. bool UpdateExistingShortName(const wchar *Name) { wchar LongPathName[NM]; DWORD Res=GetLongPathName(Name,LongPathName,ASIZE(LongPathName)); if (Res==0 || Res>=ASIZE(LongPathName)) return false; wchar ShortPathName[NM]; Res=GetShortPathName(Name,ShortPathName,ASIZE(ShortPathName)); if (Res==0 || Res>=ASIZE(ShortPathName)) return false; wchar *LongName=PointToName(LongPathName); wchar *ShortName=PointToName(ShortPathName); // We continue only if file has a short name, which does not match its // long name, and this short name is equal to name of file which we need // to create. if (*ShortName==0 || wcsicomp(LongName,ShortName)==0 || wcsicomp(PointToName(Name),ShortName)!=0) return false; // Generate the temporary new name for existing file. wchar NewName[NM]; *NewName=0; for (int I=0;I<10000 && *NewName==0;I+=123) { // Here we copy the path part of file to create. We'll make the temporary // file in the same folder. wcsncpyz(NewName,Name,ASIZE(NewName)); // Here we set the random name part. swprintf(PointToName(NewName),ASIZE(NewName),L"rtmp%d",I); // If such file is already exist, try next random name. if (FileExist(NewName)) *NewName=0; } // If we could not generate the name not used by any other file, we return. if (*NewName==0) return false; // FastFind returns the name without path, but we need the fully qualified // name for renaming, so we use the path from file to create and long name // from existing file. wchar FullName[NM]; wcsncpyz(FullName,Name,ASIZE(FullName)); SetName(FullName,LongName,ASIZE(FullName)); // Rename the existing file to randomly generated name. Normally it changes // the short name too. if (!MoveFile(FullName,NewName)) return false; // Now we need to create the temporary empty file with same name as // short name of our already existing file. We do it to occupy its previous // short name and not allow to use it again when renaming the file back to // its original long name. File KeepShortFile; bool Created=false; if (!FileExist(Name)) Created=KeepShortFile.Create(Name,FMF_WRITE|FMF_SHAREREAD); // Now we rename the existing file from temporary name to original long name. // Since its previous short name is occupied by another file, it should // get another short name. MoveFile(NewName,FullName); if (Created) { // Delete the temporary zero length file occupying the short name, KeepShortFile.Close(); KeepShortFile.Delete(); } // We successfully changed the short name. Maybe sometimes we'll simplify // this function by use of SetFileShortName Windows API call. // But SetFileShortName is not available in older Windows. return true; } #endif