libclamunrar/filcreat.cpp
d39cb658
 #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