2020-09-08 08:34:21 +00:00
|
|
|
#include "rar.hpp"
|
|
|
|
|
|
|
|
// If NewFile==NULL, we delete created file after user confirmation.
|
2023-05-19 19:24:23 +00:00
|
|
|
// It is useful if we need to overwrite an existing folder or file,
|
2020-09-08 08:34:21 +00:00
|
|
|
// but need user confirmation for that.
|
2023-05-19 19:24:23 +00:00
|
|
|
bool FileCreate(CommandData *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize,
|
2020-09-08 08:34:21 +00:00
|
|
|
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,
|
2023-05-19 19:24:23 +00:00
|
|
|
// so we preserve existing file permissions, such as NTFS permissions,
|
|
|
|
// also as "Compressed" attribute and hard links. In GUI version we avoid
|
|
|
|
// deleting an existing file for non-.rar archive formats as well.
|
2020-09-08 08:34:21 +00:00
|
|
|
uint FileMode=WriteOnly ? FMF_WRITE|FMF_SHAREREAD:FMF_UPDATE|FMF_SHAREREAD;
|
|
|
|
if (NewFile!=NULL && NewFile->Create(Name,FileMode))
|
|
|
|
return true;
|
|
|
|
|
2021-05-15 09:31:42 +00:00
|
|
|
CreatePath(Name,true,Cmd->DisableNames);
|
2020-09-08 08:34:21 +00:00
|
|
|
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
|