//============================================================================== // // See description at the end of the file // //============================================================================== // // : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : // // Values placed in this section can be customized by a user // // : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : //==== Set some constants of the algorithm === SET VALUE x_IterationCount = 20 // Maximum amount of iterations - limited by engine design SET VALUE x_Amt_1st_Images_Held = 1 // Amount of first images that are made undeletable automatically SET VALUE x_MultiVolumeFileSize = 2000*1024 // max.zise of a volume for a multi-volume archive, in MB SET STRING x_BaseImage = "" // The full filename of a base image for differential one. Can be changed // : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : // // The code placed below should not be modified // // : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : SET VALUE g_StartTime = NOWTIME // = the time of running the script SET STRING g_Version = "5.0.4" // = Version number PRINT "" PRINT "CYCLIC BACKUP ver."+string(g_Version) Print "" PRINT "Script has been started at "+STRINGTIME(value(g_StartTime)) PRINT "" CONFIRM OFF // switch off interactive mode SET STRING x_ImageDirMask = "????????.??????" // the mask for the image-directories //==== Read script parameters and set up global "non-changeabele" variables ==== SET VALUE g_Mode = PARAMETER(workmode) // = operation mode of the script SET STRING g_RootDir = STRINGPARAMETER(dir) // = %ROOTDIR% directory-name SET VALUE g_Disk = PARAMETER(dsk) // = Index of the source disk (0-based) SET VALUE g_Partn = PARAMETER(partn) // = Index of the source partition (0-based) SET VALUE g_MaxCount = PARAMETER(count) // = Quota to the maximum amount of USER images SET VALUE g_MaxSize = PARAMETER(size) // = Quota to the maximum total size of HOLD+USER images, in MB //==== Apply corrections of input params and constants ==== IF (value(g_Mode)==1) THEN // = if %MODE% = 1 then: SET VALUE x_Amt_1st_Images_Held = 0 // - no undeleteable images SET STRING x_BaseImage = "" // - all images are "full backup" ENDIF ELSE IF (value(g_Mode)==2) THEN // = if %MODE% = 2 then: SET VALUE x_Amt_1st_Images_Held = 1 // - there can be exactly 1 "full backup" undeleteable image ENDIF // - all other images are "differential" and deleteable ones ELSE SET VALUE g_Mode = 0 // = otherwise (%MODE% not in {1..2}) work in standard mode ENDELSE ENDELSE // = re-calculate g_MaxSize from MiB to kiB = IF (value(g_MaxSize)>0) THEN SET VALUE g_MaxSize = value(g_MaxSize)*1024+512 ENDIF //==== Form dir-names === SET STRING g_HoldDir = string(g_RootDir) // = %HOLDDIR% directory-name SET STRING g_UserDir = string(g_RootDir) // = %USERDIR% directory-name SET STRING aTemp00 = SUBSTRING(string(g_RootDir), STRINGLENGTH(string(g_RootDir))-1, 1) IF (NOT ((STRINGSEQUAL(string(aTemp00),"/")) // check the last char of %ROOTDIR%, to work around dirnames like "C:\" OR (STRINGSEQUAL(string(aTemp00),CODETOCHAR(92))))) THEN SET STRING g_HoldDir = string(g_HoldDir)+"/" SET STRING g_UserDir = string(g_UserDir)+"/" ENDIF SET STRING g_HoldDir = string(g_HoldDir)+"Hold" // = %HOLDDIR% directory-name SET STRING g_UserDir = string(g_UserDir)+"User" // = %USERDIR% directory-name //==== Generate dir-name of a new image === SET VALUE aTemp01 = 100000000 // Generate symbolic presentation of date in the format YYYYMMDD ) -- + YEAR( value(g_StartTime))*10000 + MONTH( value(g_StartTime))*100 +100 + DAYOFMONTH( value(g_StartTime)) SET VALUE aTemp02 = 1000000 // Generate symbolic presentation of time in the format HHMMSS ) -- + HOUR( value(g_StartTime))*10000 + MINUTE( value(g_StartTime))*100 + SECOND( value(g_StartTime)) // combine both parts into a single file-name SET STRING g_ImageDir = SUBSTRING(STRINGDEC(value(aTemp01)),1,8) // = the dir-name of the new image (in "YYYYMMDD.HHMMSS" format) +"." +SUBSTRING(STRINGDEC(value(aTemp02)),1,6) //==== Generate file-name of a new image === SET STRING g_ImageName = "d"+STRINGDEC(value(g_Disk)) IF (value(g_Partn) >=0 ) THEN // adjust filename for image type: disk/partition SET STRING g_ImageName = string(g_ImageName)+"_p"+STRINGDEC(value(g_Partn)) ENDIF SET STRING g_ImageName = string(g_ImageName)+".pbf" // = the file-name of the new image (in "d#_p#.pbf"/"d#.pbf" format) //---- Volatile global variables & flags --- SET VALUE v_HoldCount = 0 // = amount of images in the HOLD-dir SET VALUE v_UserCount = 0 // = amount of images in the USER-dir SET VALUE v_HoldSize = 0 // = total size of images in the HOLD-dir SET VALUE v_UserSize = 0 // = total size of images in the USER-dir SET VALUE v_MeetQuotas = 0 // = flag: Quotas are really satisfied SET VALUE v_OneImage = 0 // = flag: only one user-image remains SET VALUE v_EvalSize = 0 // = estimated size of a new image SET VALUE v_Imaged = 0 // =1 - a new image has been created // =0 - not even tried to create // =-1- tried but failed to create SET VALUE v_ExitCode = 0 // = Script's exit-code //==== Check Directory Structure === // Check presense of the ROOT-dir PUSHSTR(string(g_RootDir)) CALL sub_CheckTryCreateDir IF (POPNUM == 0) THEN SET VALUE v_ExitCode = 9 GOTO lb_EndOfScript ENDIF // Check presense of the HOLD-dir PUSHSTR(string(g_HoldDir)) CALL sub_CheckTryCreateDir IF (POPNUM == 0) THEN SET VALUE v_ExitCode = 8 GOTO lb_EndOfScript ENDIF // Check presense of the USER-dir PUSHSTR(string(g_UserDir)) CALL sub_CheckTryCreateDir IF (POPNUM == 0) THEN SET VALUE v_ExitCode = 8 GOTO lb_EndOfScript ENDIF // check assigned disk presense IF (NOT EXIST(value(g_Disk))) THEN PRINT "=> (!) Disk #"+STRINGDEC(value(g_Disk))+" not present!" PRINT "" SET VALUE v_ExitCode = 7 GOTO lb_EndOfScript ENDIF // check assigned partition presense (if it's required) IF (value(g_Partn)>=0) THEN IF (NOT EXIST(value(g_Disk), value(g_Partn))) THEN PRINT "=> (!) Partition #"+STRINGDEC(value(g_Partn)) PRINT " on Disk #"+STRINGDEC(value(g_Disk))+" not present!" PRINT "" SET VALUE v_ExitCode = 6 GOTO lb_EndOfScript ENDIF IF (EXTENDED(value(g_Disk), value(g_Partn))) THEN PRINT "=> (!) This version of script cannot backup Extended Partition !" PRINT "" SET VALUE v_ExitCode = 5 GOTO lb_EndOfScript ENDIF ENDIF //==== Evaluate HOLD-dir === PUSHSTR(string(g_HoldDir)) CALL sub_EvaluateDir SET VALUE v_HoldSize = POPNUM SET VALUE v_HoldCount = POPNUM // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ //==== Apply %MODE% ==== IF (value(g_Mode)==1) THEN // = if %MODE% = 1 then: SET STRING x_BaseImage = "" // - there no undeleteable images ENDIF // - all images are "full backup" ELSE IF (value(g_Mode)==2) THEN IF (value(v_HoldCount)==0) THEN // = if %MODE% = 2 then: SET STRING x_BaseImage = "" // - the 1-st image is undeleteable and "full backup" ENDIF ELSE PUSHSTR(string(g_HoldDir)) // - all other images are "differential" from that one CALL sub_FindLatestImage SET STRING x_BaseImage = POPSTR PRINT " --> Use the base-Image: " + string(x_BaseImage) PRINT "" ENDELSE ENDIF ENDELSE // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ //= Check amount of undeleteable images IF (value(v_HoldCount) < value(x_Amt_1st_Images_Held)) THEN CALL sub_MakeUndeleteableImage GOTO lb_EndOfScript ENDIF // Define parameters of the backup operation IMG = string(g_UserDir)+"/"+string(g_ImageDir)+"/"+string(g_ImageName) UNSELECT ALL SELECT DISK value(g_Disk) IF (value(g_Partn) >=0 ) THEN SELECT PARTITION value(g_Partn) ENDIF ELSE SELECT PARTITIONS ALL ENDELSE //------------------------------------------------------------------------------------------- // this code has been added in the ver 5.0.1: //------------------------------------------------------------------------------------------- // - does size-evaluation for basic (full) images // - sets the evaluation size to a fake value for diff.images (the fake value = 1MB). // IF ((value(g_Mode) ==2) AND (value(v_HoldCount) >0)) THEN SET VALUE v_EvalSize = 1024 ENDIF ELSE // this is the normal operation of the script - evaluate size of a new image OPTIONS AUTONAMES NOTEMPFILES ESTIMATION(3, v_EvalSize) // Make image-size estimation ( level 3 = by compressing 10% of data) STORE ENDELSE //------------------------------------------------------------------------------------------- SET VALUE v_EvalCount = 1 //==== Run loop of the main process ==== DO PUSHSTR( string(g_UserDir) ) //==== Evaluate USER-dir === CALL sub_EvaluateDir SET VALUE v_UserSize = POPNUM SET VALUE v_UserCount = POPNUM //==== Set the flag "one image" === SET VALUE v_OneImage = 0 IF (value(v_UserCount)<=1) THEN SET VALUE v_OneImage = 1 ENDIF //==== Evaluate against Quotas === SET VALUE v_MeetQuotas = 1 IF ( ( (00) THEN SET VALUE v_EvalSize = 0 SET VALUE v_EvalCount = 0 SET VALUE v_EvalSize = value(v_EvalSize) SET VALUE v_EvalCount = value(v_EvalCount) ENDIF ELSE PUSHSTR(string(g_UserDir)+"/"+string(g_ImageDir)) CALL sub_DeleteDir PUSHSTR(string(g_UserDir)+"/"+"00000000.000000") CALL sub_DeleteDir ENDELSE ENDIF ENDIF ELSE PRINT "=> Quotas aren't satisfied!" PRINT "" PUSHSTR(string(g_UserDir)) // try to delete an oldest image CALL sub_DelOldest IF (POPNUM == 0) THEN PRINT "=> (!) No images found and space wasn't freed" PRINT "" PRINT " Remove undesired contents of "+string(g_UserDir)+" by yourself." PRINT "" SET VALUE x_IterationCount = 0 ENDIF ENDELSE SET VALUE x_IterationCount = value(x_IterationCount) -1 WHILE ( (value(x_IterationCount)>0) AND (value(v_MeetQuotas)==0) AND (value(v_OneImage)==0) AND (value(v_Imaged)>=0) ) //==== Analyze results === lb_EndOfScript: IF (value(v_Imaged)==-1) THEN PRINT "=> (!) There was an error that prevented image creation!" SET VALUE v_ExitCode = 3 ENDIF IF (value(v_Imaged)==0) THEN SET VALUE v_ExitCode = 2 PRINT "=> (!) The new image wasn't created" IF (value(x_IterationCount)==0) THEN PRINT " because of too much old images were deleted." PRINT "" PRINT " Run the script once again to create a new image." SET VALUE v_ExitCode = 1 ENDIF ENDIF PRINT "" PRINT "Script has been finished at "+STRINGTIME( NOWTIME ) PRINT "========================" PRINT "" //==== Copy log-files to the ROOT-dir === SET STRING aTemp00 = SUBSTRING(string(g_RootDir), STRINGLENGTH(string(g_RootDir))-1, 1) IF (NOT ((STRINGSEQUAL(string(aTemp00),"/")) OR (STRINGSEQUAL(string(aTemp00),CODETOCHAR(92))))) THEN SET STRING g_RootDir = string(g_RootDir)+"/" ENDIF // delete engine's log-files FILEDELETE(string(g_RootDir)+"STUBACT.LOG") FILEDELETE(string(g_RootDir)+"PWLOG.TXT") // copy script's log-file FILEMERGE("PSI.OUT", string(g_RootDir)+"LOG.txt") FILECOPY("PSI.OUT", string(g_RootDir)+"LOG.txt") // copy engine's log-files FILECOPY("STUBACT.LOG",string(g_RootDir)+"STUBACT.LOG") FILECOPY("PWLOG.TXT", string(g_RootDir)+"PWLOG.TXT") EXIT(value(v_ExitCode)) //------- // EXIT-CODES: // 0 = OK // 1 = too much "old" images were deleted // 2 = not enough space to save a new image // 3 = other error while creating a new image // 4 = not used // 5 = attempt to backup Extended Partition // 6 = invalid partition index // 7 = invalid disk index // 8 = cannot create %HOLD% or %USER% // 9 = invalid %ROOT% // //============================================================================== // sub_CheckTryCreateDir // Checks presense of a directory. // If dir was absent, it makes one attempt to create dir // Parameters: // - (1) - dir-name // Results: // - (1) - dir presense flag 0/1 //============================================================================== // sub_CheckTryCreateDir: SET STRING aDir = POPSTR SET VALUE aRes = 1 // Check presense of the optional (?) HOLD-dir IF (NOT FileIsDir(string(aDir))) THEN PRINT " -> (!) Directory not found: "+string(aDir) PRINT "" FILEMAKEDIR(string(aDir)) // try to create a dir IF (NOT FileIsDir(string(aDir))) THEN PRINT " -> (!) Failed to make directory: "+string(aDir) PRINT "" SET VALUE aRes = 0 ENDIF ELSE PRINT " -> Directory has been created: "+string(aDir) PRINT "" ENDELSE ENDIF PUSHNUM(value(aRes)) ENDCALL //============================================================================== // sub_MakeUndeleteableImage // Makes an image of a given partition // Parameters: // - (none) // Results: // - (none) //============================================================================== sub_MakeUndeleteableImage: // Print name of a new image PRINT " => Create the undeleteable image: " PRINT string(g_HoldDir)+"/"+string(g_ImageDir)+"/"+string(g_ImageName) PRINT "" PUSHSTR(string(g_HoldDir)) CALL sub_MakeImage ENDCALL //============================================================================== // sub_MakeImage // Makes an image of a given partition // Parameters: // - (1) - name of the directory, where the image should be created ( format {g_ImageDir}.{g_ImageName} ) // Results: // - (none) // Notes: // - also uses the x_BaseImage value to re-define BASE parameter, in case v_LastHoldImage is not empty (<>"") // in this case it creates the "full backup" image, otherwisethe new image is "differential" //============================================================================== // sub_MakeImage: // Print name of a new image SET STRING aBaseDir = POPSTR // = dir-name of a base dir SET STRING aTempDir = string(aBaseDir)+"/"+"00000000.000000" // = temp-dir, for temp-image SET STRING aFinalDir= string(aBaseDir)+"/"+string(g_ImageDir) // = final dir-name for the image PRINT " --> Creating new image: "+string(aFinalDir) PRINT "" PUSHSTR(string(aTempDir)) CALL sub_CheckTryCreateDir IF (POPNUM == 0) THEN PRINT " --> Failed to make temp directory. The image was not created in: "+string(aBaseDir) PRINT "" ENDCALL ENDIF SET STRING aTargetName = "Disk="+STRINGDEC(value(g_Disk)) IF (value(g_Partn) >=0 ) THEN SET STRING aTargetName = string(aTargetName)+",Partition="+STRINGDEC(value(g_Partn)) ENDIF // Define backup parameters IMG = string(aTempDir)+"/"+string(g_ImageName) BASE = string(x_BaseImage) //=== select disk & partition === UNSELECT ALL // unselect previous SELECT DISK value(g_Disk) // select disk IF (value(g_Partn) < 0 ) THEN SELECT PARTITIONS ALL // select all partitions to backup entire disk ENDIF ELSE SELECT PARTITION value(g_Partn) // select a single partition ENDELSE SET STRING aLabel = "The image of "+string(aTargetName) +" is auto-created by the CYCLIC BACKUP script at " +STRINGTIME(value(g_StartTime)) //=== Now choose between "full" and "differential" backup === IF (STRINGLENGTH(string(x_BaseImage))>0) THEN // create a differential backup SET STRING aLabel = string(aLabel)+"\n"+"Image is differential from "+string(x_BaseImage) // CODETOCHAR(10)+CODETOCHAR(13) OPTIONS LABEL = string(aLabel) AUTONAMES MFS = value(x_MultiVolumeFileSize) NOTEMPFILES NOSIGNATURE BASE_PWD="" USEVSS STORE ENDIF ELSE // create a full backup OPTIONS LABEL = string(aLabel) AUTONAMES MFS = value(x_MultiVolumeFileSize) NOTEMPFILES NOSIGNATURE USEVSS STORE ENDELSE // Make a backup image APPLY ALL //==== Analyze errors === SET VALUE v_Imaged = 0 // reset creation flag IF ( ERRORCODE(1) == 0 ) THEN // 0: - no errors SET VALUE v_Imaged = 1 // set creation flag FILERENAME(string(aTempDir),string(aFinalDir)) // rename temp-dir to the final-dir PRINT " --> The image has been created" PRINT "" //==== update the image name(path) in the ArcDataBase === base = string(aTempDir) img = string(aFinalDir) base update ENDIF ELSE // process "no space" error(s) IF ( (ERRORCODE(1) == 0x10023) OR (ERRORCODE(1) == 0x10024) OR (ERRORCODE(1) == 0x10091)) THEN SET VALUE v_Imaged = -1 // -1="fatal" error, 0="non-fatal" error PRINT " --> (!) Not enough space to store new image" PRINT "" ENDIF ELSE // process other errors SET VALUE v_Imaged = -1 STRERROR(ERRORCODE(1)) PRINT "" ENDELSE PUSHSTR(string(aTempDir)) CALL sub_DeleteDir ENDELSE // warning suppression trick SET VALUE v_Imaged = value(v_Imaged) ENDCALL //~ ~ ~ ~ ~ ~ ~ // ERRORCODES: // 0x10023 - Cannot create/open file // 0x10024 - Insufficient space to create archive // 0x10091 - Aborted by user // 0x1100B - restart is required to complete the operation // (error 0x1100B currently has no specific handling) //~ ~ ~ ~ ~ ~ ~ //============================================================================== // sub_EvaluateDir // Makes evaluation of a directory: (1) count of images and (2) total size // Parameters: // - (1) - directory name // Results: // - (1) - total size of images // - (2) - count of images //============================================================================== // sub_EvaluateDir: SET STRING Dir1 = POPSTR SET VALUE Cnt1 = 0 SET VALUE Cnt1a = 0 SET VALUE Sz1 = 0 SET STRING Dir1a = "" SET STRING F1 = "" //=== for empty dir-names, return zeroes IF (STRINGLENGTH(string(Dir1))==0) THEN PUSHNUM(0) PUSHNUM(0) ENDIF //=== Evaluate amount of images XFIND OPTIONS RESULT = F1 RECURSIVE = DIRECTORIES FIRST RECURSIVE LEVEL = 1 START = string(Dir1) MASK = string(x_ImageDirMask) SEARCH = WIDE FILES = OFF DIRECTORIES = ON ENDOPTIONS BEGIN SET STRING Dir1a = string(Dir1) +"/"+ string(F1) PUSHSTR(string(Dir1a)) // store found dir-names in the stack SET VALUE Cnt1 = value(Cnt1)+1 ENDXFIND IF (value(Cnt1)==0) THEN //=== return zeroes if no matches PUSHNUM(0) PUSHNUM(0) ENDIF SET VALUE Cnt1a = value(Cnt1) DO SET STRING Dir1a = POPSTR SET STRING F1 = "" XFIND OPTIONS RESULT = F1 RECURSIVE = FILES FIRST RECURSIVE LEVEL = 0 START = string(Dir1a) MASK = "*.*" SEARCH = WIDE FILES = ON DIRECTORIES = OFF ENDOPTIONS BEGIN SET VALUE Sz1 = value(Sz1)+FILESIZE(string(Dir1a)+"/"+string(F1))+1 ENDXFIND SET VALUE Cnt1a = value(Cnt1a) - 1 WHILE (value(Cnt1a)>0) PUSHNUM(value(Cnt1)) // return amount of elements PUSHNUM(value(Sz1)) // return total size of elements ENDCALL //============================================================================== // sub_DelOldest // Finds and deletes the oldest image in the directory // Parameters: // - (1) - the name of the directory with images // Returns: // - (1) - flag of deletion 0/1 //============================================================================== // sub_DelOldest: // Read params and initialize variables SET STRING Dir2 = POPSTR SET VALUE aDate = 0 SET VALUE aTime = 0 SET VALUE mDate = 99999999 SET VALUE mTime = 999999 SET STRING F2 = "" SET STRING mF2 = "" // scan images XFIND OPTIONS RESULT = F2 RECURSIVE = FILES FIRST RECURSIVE LEVEL = 1 START = string(Dir2) MASK = string(x_ImageDirMask) SEARCH = WIDE FILES = OFF DIRECTORIES = ON ENDOPTIONS BEGIN // extract YYYYMMMDD from file-name PUSHSTR(SUBSTRING(string(F2),0,8)) CALL sub_StrToInt SET VALUE aDate = POPNUM IF (value(aDate)>0) THEN // extract HHMMSS from file-name PUSHSTR( SUBSTRING(string(F2),9,6) ) CALL sub_StrToInt SET VALUE aTime = POPNUM // search for oldest date+time IF (value(aTime)>=0) THEN IF ( (value(aDate) Deleting dir: "+string(F2) PRINT "" // delete an image found PUSHSTR(string(F2)) CALL sub_DeleteDir //------------------------------------------------------------------------------------------- // this code has been added in the ver 5.0.1: //------------------------------------------------------------------------------------------- // - notify archive DB about the image deleting base = string(F2) img = "" base update //------------------------------------------------------------------------------------------- // return 1, report about deletion PUSHNUM(1) ENDCALL //============================================================================== // sub_StrToInt // Translates a string into its numeric representation // - !! No ANY checks is performed. Signed numbers aren't supported !! - // IN: // - (1) - a string to translate // OUT: // - (1) - a result //============================================================================== sub_StrToInt: SET STRING aStr = POPSTR SET VALUE StrLen = STRINGLENGTH( string(aStr)) SET VALUE aRes = 0 SET VALUE indx = 0 SET VALUE aTmp = 0 SET STRING aChar = "" IF (value(StrLen)<=0) THEN PUSHNUM(0x80000000) ENDCALL ENDIF DO SET STRING aChar = SUBSTRING(string(aStr), value(indx), 1) SET VALUE aTmp = CHARTOCODE(string(aChar))-CHARTOCODE("0") IF ((value(aTmp)>=0) AND (value(aTmp)<=9)) THEN SET VALUE aRes = value(aRes)*10 + value(aTmp) SET VALUE indx = value(indx)+1 ENDIF ELSE SET VALUE aRes = 0x80000000 SET VALUE indx = value(StrLen) ENDELSE WHILE (value(indx)=0) THEN SET STRING F3 = string(Dir3) +"/"+ DIRELEMENT(string(Dir3),value(i)) FILEDELETE(string(F3)) SET VALUE i = value(i)-1 ENDIF WHILE (value(i)>=0) // scan and delete sub-dirs SET VALUE i = DIRCONTENTS(string(Dir3))-1 DO IF (value(i)>=0) THEN SET STRING F3 = string(Dir3) +"/"+ DIRELEMENT(string(Dir3),value(i)) PUSHSTR(string(Dir3)) PUSHNUM(value(i)) PUSHSTR(string(F3)) CALL sub_DeleteDir // recursive deletion SET VALUE i = POPNUM-1 SET VALUE i = value(i) SET STRING Dir3 = POPSTR ENDIF WHILE (value(i)>=0) FILEDELETEDIR(string(Dir3)) IF (FILEEXIST(string(Dir3))) THEN PRINT " -> (!) Dir wasn't deleted: "+string(Dir3) PRINT "" ENDIF ELSE PRINT " -> Deleted: "+string(Dir3) PRINT "" ENDELSE ENDCALL //============================================================================== // sub_FindLatestImage // Finds the latest image in the directory // Parameters: // - (1) - the name of the directory with images // Returns: // - (1) - the name of the latest image //============================================================================== // sub_FindLatestImage: // Read params and initialize variables SET STRING Dir4 = POPSTR SET VALUE aDate2 = 0 SET VALUE aTime2 = 0 SET VALUE lDate = 0 SET VALUE lTime = 0 SET STRING F3 = "" SET STRING aRes4 = "" // scan images XFIND OPTIONS RESULT = F3 RECURSIVE = FILES FIRST RECURSIVE LEVEL = 1 START = string(Dir4) MASK = string(x_ImageDirMask) SEARCH = WIDE FILES = OFF DIRECTORIES = ON ENDOPTIONS BEGIN // extract YYYYMMMDD from file-name PUSHSTR(SUBSTRING(string(F3),0,8)) CALL sub_StrToInt SET VALUE aDate2 = POPNUM // extract HHMMSS from file-name PUSHSTR( SUBSTRING(string(F3),9,6) ) CALL sub_StrToInt SET VALUE aTime2 = POPNUM // search for oldest date+time IF ( (value(aDate2)>value(lDate)) OR( (value(aDate2)==value(lDate)) AND (value(aTime2) >value(lTime)))) THEN SET VALUE lDate = value(aDate2) SET VALUE lTime = value(aTime2) SET STRING aRes4 = string(F3) ENDIF ENDXFIND IF ((value(lDate)+value(lTime))<=0) THEN // check if any valid dir-name was observed PUSHSTR("") ENDCALL ENDIF SET STRING Dir4 = string(Dir4) +"/"+ string(aRes4) SET STRING aRes4 = string(Dir4) +"/"+ string(g_ImageName) IF (NOT FILEEXIST(string(aRes4))) THEN SET STRING aRes4 = "" ENDIF // return image dir-name PUSHSTR(string(aRes4)) ENDCALL //################################################## EXIT(0) //============================================================================== // DESCRIPTION // // The presumed structure of the Backup-Zone: // // \%ROOTDIR% // \Hold // \{datetime1}\ // \{datetime3}\ // \{datetime6}\ // \User // \{datetime2}\ // \{datetime7}\ // \{datetime9}\ // . . . // // where {datetime} is the date+time of the image creation, // written in the following format: // YYYYMMDD.HHMMSS // //============================================================================== // // The presumed STRATEGY is the following: // // - the user is allowed to hold 1 image, even in case it exceeds quotas. // - the user is allowed to exceed quotas until the backup completes. // - quota check-outs are performed before and after the backup process. // - HOLD images are never deleted. // - if quotas exceeded, the oldest image(s) are deleted, excepting HOLD ones. // - the script tries to create a new image even in case quotas are exceeded. // - image creation abandoned in case many (~100) old images was deleted to satisfy quotas // //============================================================================== // // Parameters and Global variables // The script should be started in the following manner: // // SCRIPTS.EXE cyclic.psl -s:dir= -p:count= -p:size= -p:dsk= -p:partn= -p:workmode= // // Hereafter the explanation of parameters: // // === %ROOTDIR% === // Hereafter it's presumed that the valid name of the %ROOTDIR% directory // is stored in the g_RootDir string-variable. // In this script, it's assumed that %ROOTDIR% value is passed in script's // string-parameter named "dir": // -s:dir= // // === %MAXCOUNT% Quotas - to the maximum amount of images === // The %MAXCOUNT% is the per-user maximum amount of images to be saved. // Hereafter it's presumed that %MAXCOUNT% is stored in the g_MaxCount variable // In this script, it's assumed that %MAXCOUNT% value is passed in script's // parameter named "count": // -p:count= // // Interpretation of %MAXCOUNT% values: // m>0 - amount of stored images must not exceed given value // m<=0 - ignore this type of Quotas // // === %MAXSIZE% Quotas - to the maximum total size of images === // The %MAXSIZE% is the per-user maximum total size of images to be saved. // Hereafter it's presumed that %MAXSIZE% is stored in the g_MaxSize variable // In this script, it's assumed that %MAXSIZE% value is passed in the script's // parameter named "size": // -p:size= // // Interpretation of %MAXSIZE% values: // z>0 - size quotas for stored images in MB // z<=0 - ignore this type of Quotas // // === %DISK% - the index of the Disk to be imaged === // === %PARTITION% - the index of the partition to be imaged === // These values are stored in the g_Disk and g_Partn variables, respectively. // Actual values are defined in the script's parameters "dsk" and "partn": // -p:dsk= -p:partn= // For both values, the index is zero-based one. // The index of a target partition "partn" can be a negative value. // In this case, the script will try to backup entire hard disk assigned by the "dsk" parameter. // // === %MODE% - operation mode of the script == // This value is stored in the g_Mode variable and its value affects on the behaviour of the script. // In this script, it's assumed that %MODE% value is passed in the script's // parameter named "mode": // -p:workmode= // // Interpretation of %MODE% values: // m=0 - work in the standard mode, and use default internal values // m=1 - amount of undeleteable images is forsedly set to 0 // m=2 - amount of undeleteable images is forsedly set to 1, // and all deleteable images are differential ones // m=other values - same as m=0 // //============================================================================== // HISTORY //----- // Ver.1.0.2: produced from 1.0.0 (unfixed SELECT PARTITIONS) // fixed: // - if parameter "count" =0, all images were created in Hold-Dir // fix: sub_EvaluateDir, originally it set dirCount=0 if gMaxCount=0. // it lead to problems with checkout of x_Amt_1st_Images_Held //----- // Ver.1.0.3: produced from 1.0.2 (unfixed SELECT PARTITIONS) // added: // - CONFIRM OFF has been added // - "dsk" index is checked for disk presense // - "partn" index is checked for partition presense //----- // Ver.1.0.4: produced from 1.0.3 (unfixed SELECT PARTITIONS) // fixed: // - image-directory YYYYMMDD.HHMMSS contains invalid day-number (=actual day +1) //----- // Ver.2.1.5: produced from 1.0.4 // added: // - uses the fixed semantic of SELECT PARTITIONS // !! and now can work only under the fixed version of PSI !! // - a new method of log copying, which is tolerant to "no-space" issues // - new scheme of image creation (workaround for dir-deletion issue) // - multiple code re-structurizations // - distinguishable exit-codes //----- // Ver.2.1.6: produced from 2.1.5 // fixed: // - The temporary directory is created before producing the image. // This is because the engine fails to create a directory+image withby an UNC pathname. // Every new image is created in a temp dir "00000000.000000". // Upon successfull image creation the temp-dir is renamed to the .