;; ; @(#) filedel.asm - File and directory complete deletion resident utility. ; (c) 1998 Ivan Maidanski http://ivmai.chat.ru ; Freeware program source. All rights reserved. ;; ; Language: ASM8086 (for IBM PC/AT, MS-DOS v3.0+, COM file) ; Tested with: Turbo Assembler v3.1, Turbo Link v5.1 ; Last modified: 1998-09-05 14:40:00 GMT+04:00 ;; NAME File_Delete_TSR ; tasm filedel.asm ; tlink /t filedel.obj ; Comment: When this program installed, it handles normal DOS functions ; (delete, rename, truncate, etc.) in such a way that all the erased file ; data will be lost (i.e. unrecoverable). .MODEL SMALL .CODE .8086 Null LABEL NEAR ; Program constants (may be changed) TSR@DefBufLen= 20h ; assume 512-byte default working buffer TSR@MaxBufLen= 400h ; assume 16K-byte max working buffer size TSR@BlankWord= 0F6F6h ; value used for file blanking ; PSP segment ORG 2Ch PSP@EnvSeg DW ? ; environment segment ORG 80h PSP@CmdLine DB 80h DUP(?) ; program command line ; RAM-resident part variables ORG 0 Int2Fh@SwStartupRec DW 9h DUP(?) Int2Fh@InstanceRec DW 5h DUP(?) ; Program start point ORG 100h Start: jmp Main ; RAM-resident part local stack EVEN TSR@StackTop LABEL WORD ; RAM-resident part code ASSUME ds:NOTHING,es:NOTHING EVEN Int23h PROC FAR ; Comment: CTRL+C intercept handler (residental/applicational) ; Expects: - ; Returns: - ; Uses: - iret ; disable CTRL+C processing Int23h ENDP EVEN Int24hApp PROC FAR ; Comment: Critical error intercept handler (applicational) ; Expects: ah,al,di,bp:[si],Stack ; Returns: al - action code (ignore,retry,abort,fail) ; Uses: al mov al,3h ; - fail iret Int24hApp ENDP EVEN Int24hRes PROC FAR ; Comment: Critical error intercept handler (residental) ; Expects: ah,al,di,bp:[si],Stack ; Returns: al - action code (ignore,retry,abort,fail) ; Uses: al push cx mov cx,sp mov ss,cs:[TSR@StackTop-12h] ; restore application stack pointer mov sp,cs:[TSR@StackTop-10h] dec byte ptr cs:[TSR@StackTop-1h] ; open gates pushf call dword ptr cs:[TSR@StackTop-0Ah] cmp al,2h ; abort ? je Int24hRes@SkipGate inc byte ptr cs:[TSR@StackTop-1h] ; close gates Int24hRes@SkipGate: push cs ; set ss=cs pop ss ; restore TSR stack pointer mov sp,cx pop cx iret Int24hRes ENDP EVEN Int2Fh PROC FAR ; Comment: Multiplex interrupt handler ; Expects: ah - ID number, al=0 - installation check, ; ax=1605h - identify instance data (Windows 386 API),..., ; ax=4B05h - identify instance data (Task Switcher API),..., ; es=0 & bx=0 (identify) ; Returns: ah - ID number (check), al=1h - installed (check), ; es:[di] - ASCIIZ ID string (check), es:[bx] - SwStartupRec (identify),... ; Uses: - (al,di,es)/(bx,cx,si,ds,es) cli cmp ax,1605h ; starting Windows 386 ? je Int2Fh@MultiEnv cmp ax,4B05h ; starting DOS Task Switcher ? je Int2Fh@MultiEnv cmp ah,0 ORG $-1h Int2Fh@IDNumber DB 0 je Int2Fh@Check DB 0EAh ; jmp far Int2Fh@Old DD 0 Int2Fh@MultiEnv: pushf call cs:[Int2Fh@Old] sti mov word ptr cs:[Int2Fh@SwStartupRec+2h],bx mov bx,OFFSET Int2Fh@InstanceRec mov word ptr cs:[bx],0 ; make copy of TSR Stack for each DOS session mov word ptr cs:[bx+2h],cs mov word ptr cs:[bx+4h],TSR@StackTop-Null mov word ptr cs:[bx+6h],0 ; end of areas definition mov word ptr cs:[bx+8h],0 mov bx,OFFSET Int2Fh@SwStartupRec mov word ptr cs:[bx+4h],es ; chain to previous list mov word ptr cs:[bx+0Eh],OFFSET Int2Fh@InstanceRec mov word ptr cs:[bx+10h],cs mov word ptr cs:[bx],3h ; set target Windows version mov word ptr cs:[bx+6h],0 mov word ptr cs:[bx+8h],0 mov word ptr cs:[bx+0Ah],0 mov word ptr cs:[bx+0Ch],0 jmp short Int2Fh@SetSeg Int2Fh@Check: cmp al,0 jne Int2Fh@Ret inc ax ; indicate that ID number taken mov di,OFFSET Int2Fh@IDStr Int2Fh@SetSeg: push cs ; set es=cs pop es Int2Fh@Ret: iret Int2Fh ENDP EVEN Int21h PROC FAR ; Comment: DOS functions intercept handler ; Expects: ah - function number (13h,16h,3Ah,3Ch,40h,41h,56h,6Ch),... ; Returns: CF - error flag, ax - error code if CF=1,... ; Uses: - (ax,cx,CF) cli ; begin critical section pushf ; preserve flags cmp ah,3Ah ; check for RMDIR jb Int21h@TestLow je Int21h@Init cmp ah,41h ; check for Delete (UNLINK) jb Int21h@TestHigh je Int21h@Init cmp ah,4Ch ; check for Terminate jne Int21h@TestLast mov byte ptr cs:[TSR@StackTop-1h],0 ; abort current operation jmp short Int21h@FarJump ; (open gates) Int21h@TestLast: cmp ah,56h ; check for Move je Int21h@Init cmp ax,6C00h ; check for SmartOpen jne Int21h@FarJump test dl,2h jnz Int21h@Init jmp short Int21h@FarJump Int21h@TestLow: cmp ah,13h ; check for FCBDelete je Int21h@Init cmp ah,16h ; check for FCBCreate je Int21h@Init jmp short Int21h@FarJump Int21h@TestHigh: cmp ah,3Ch ; check for CreateFile je Int21h@Init cmp ah,40h ; check for Truncate jb Int21h@FarJump jcxz Int21h@Init Int21h@FarJump: popf DB 0EAh ; jmp far Int21h@Old DD 0 Int21h@Init: cmp byte ptr cs:[TSR@StackTop-1h],0 ; TSR busy ? jz Int21h@Prepare cmp ah,39h jae Int21h@Busy popf mov al,0FFh ; set error flag jmp Int21h@Ret Int21h@Busy: popf mov ax,5h ; set "access denied" error code stc jmp Int21h@Ret Int21h@Prepare: inc byte ptr cs:[TSR@StackTop-1h] ; close gates popf push es ; save all registers push ds push di push si push dx push cx push bx push ax mov cs:[TSR@StackTop-12h],ss mov cs:[TSR@StackTop-10h],sp push cs ; set up local stack pop ss mov sp,OFFSET TSR@StackTop-12h sti ; end critical section pushf ; set error trapping handlers push ax push ds push dx push es push bx mov ax,3523h ; save current int 23h handler addr pushf cli call cs:[Int21h@Old] mov cs:[TSR@StackTop-4h],es mov cs:[TSR@StackTop-6h],bx mov ax,3524h ; save current int 24h handler addr pushf cli call cs:[Int21h@Old] mov cs:[TSR@StackTop-8h],es mov cs:[TSR@StackTop-0Ah],bx pop bx pop es push cs ; set ds=cs pop ds mov ax,2523h ; set up new int 23h handler mov dx,OFFSET Int23h pushf cli call cs:[Int21h@Old] mov ax,2524h ; set up new int 24h handler mov dx,OFFSET Int24hRes pushf cli call cs:[Int21h@Old] pop dx pop ds mov ax,cs ; store working buffer segment and length add ax,(TSR@DefBuffer-Null+0Fh)/10h mov word ptr cs:[TSR@StackTop-0Ch],ax mov word ptr cs:[TSR@StackTop-0Eh],TSR@DefBufLen pop ax IF (TSR@MaxBufLen GT TSR@DefBufLen) push ax push bx cmp ah,3Ah ; working buffer not needed ? je Int21h@StdCall cmp ah,56h je Int21h@StdCall pushf ; get avail memory size mov ah,48h mov bx,0FFFFh cli call cs:[Int21h@Old] and bl,0E0h ; align to sector size cmp bx,TSR@DefBufLen ; no available memory ? jbe Int21h@StdCall cmp bx,TSR@MaxBufLen ; available memory size less than maximal ? jb Int21h@Alloc mov bx,TSR@MaxBufLen Int21h@Alloc: pushf ; allocate working buffer mov ah,48h cli call cs:[Int21h@Old] jc Int21h@StdCall ; error ? mov word ptr cs:[TSR@StackTop-0Ch],ax ; store new working buffer seg & len mov word ptr cs:[TSR@StackTop-0Eh],bx pop bx pop ax popf ; process required function call AllFunc pushf push ax ; free allocated working buffer mov es,word ptr cs:[TSR@StackTop-0Ch] pushf mov ah,49h cli call cs:[Int21h@Old] jmp short Int21h@RestTrap Int21h@StdCall: pop bx pop ax ENDIF popf ; process required function call AllFunc pushf ; restore error trapping handlers push ax Int21h@RestTrap: mov ax,2524h ; restore int 24h handler lds dx,dword ptr cs:[TSR@StackTop-0Ah] pushf cli call cs:[Int21h@Old] mov ax,2523h ; restore int 23h handler lds dx,dword ptr cs:[TSR@StackTop-6h] pushf cli call cs:[Int21h@Old] pop ax popf cli ; disable interrupts pop ss ; restore stack mov sp,cs:[TSR@StackTop-10h] pop si ; ignore opcode pop bx pop di ; ignore additional parameter pop dx pop si pop di pop ds pop es mov byte ptr cs:[TSR@StackTop-1h],0 ; open gates Int21h@Ret: push bp ; store CF (error flag) mov bp,sp pushf add bp,6h ror word ptr ss:[bp],1h popf rcl word ptr ss:[bp],1h pop bp iret Int21h ENDP AllFunc PROC NEAR ; Comment: TSR main functional part ; Expects: ah - called function (13h,16h,3Ah,3Ch,40h,41h,56h,6Ch only),... ; Returns: CF - error flag, ax - error code if CF=1,... ; Uses: bx,dx,si,di,ds,es,(ax,cx,CF) push cx pushf ; preserve CF cmp ah,40h ; Truncate ? jb AllFunc@Other je AllFunc@Trunc popf cmp ah,56h ; Move or Delete ? jb AllFunc@Delete je AllFunc@Move pop cx call SmartOpen jmp short AllFunc@OpenRet AllFunc@Move: push ax ; preserve opcode call MoveFile jmp short AllFunc@Restore AllFunc@Delete: push ax ; preserve opcode call DeleteFile jmp short AllFunc@Restore AllFunc@Trunc: popf call BlankFile jc AllFunc@Ret sub ax,ax ; CF=0 jmp short AllFunc@Ret AllFunc@Other: cmp ah,3Ah ; RMDIR or Create ? jb AllFunc@FCBFunc je AllFunc@Remove popf call CreateFile jmp short AllFunc@Ret AllFunc@Remove: popf push ax ; preserve opcode stc ; remove directory call Remove AllFunc@Restore: pop bx ; restore opcode jc AllFunc@Ret mov ax,bx jmp short AllFunc@Ret AllFunc@FCBFunc: cmp ah,13h ; Delete or Create via FCB ? je AllFunc@FCBDelete call FCBCreate mov ax,1600h jmp short AllFunc@FCBDone AllFunc@FCBDelete: mov ah,2Fh ; query current DTA pushf cli call cs:[Int21h@Old] push es push bx call FCBDelete pop dx pop ds pushf mov ah,1Ah ; restore current DTA pushf cli call cs:[Int21h@Old] popf mov ax,1300h AllFunc@FCBDone: jnc AllFunc@FCBRet dec al ; set error flag AllFunc@FCBRet: popf AllFunc@Ret: pop cx AllFunc@OpenRet: ret AllFunc ENDP FCBDelete PROC NEAR ; Comment: New "Delete files via FCB" DOS function (13h) ; Expects: ds:[dx] - unopened FCB ; Returns: CF - error flag ; Uses: ax,bx,cx,dx,si,di,ds,es,DTA sub sp,2Ch ; reserve room for extended FCB push ss ; set es=ss pop es mov di,sp mov byte ptr es:[di],0 ; use ordinary FCB cld ; copy unopened FCB (drive,name,extension) mov si,dx add di,7h push di mov cx,6h rep movsw pop si sub sp,22h ; reserve room for DTA push ss ; set ds=ss pop ds mov dx,sp mov ah,1Ah ; set up new DTA inc dx pushf cli call cs:[Int21h@Old] sub sp,10h ; reserve room for file name mov di,sp mov ah,11h ; find first matching file xchg dx,si mov cx,1h ; set error flag FCBDelete@Loop: pushf cli call cs:[Int21h@Old] inc al ; no (more) matching files ? jz FCBDelete@Restore test byte ptr ds:[si+0Ch],1Eh ; check file attribute mov ah,12h ; find next file jnz FCBDelete@Loop push cx push dx mov dx,si call FCBCopyName push dx mov dx,di call DeleteFile push ss ; set es=ss pop es mov di,dx pop si pop dx pop cx mov ah,12h ; find next matching file jcxz FCBDelete@Loop mov cl,0 ; indicate that file was found sbb cl,0 jmp short FCBDelete@Loop FCBDelete@Restore: cmp cl,0FFh ; access denied ? jne FCBDelete@Ret mov dx,di call DeleteFile rcl cl,1h ; preserve error flag FCBDelete@Ret: add sp,5Eh rcr cl,1h ; set error flag if not found or access denied ret FCBDelete ENDP FCBCreate PROC NEAR ; Comment: New "Create file via FCB" DOS function (16h) ; Expects: ds:[dx] - unopened FCB ; Returns: CF - error flag ; Uses: ax,cx,si,di,es push ds push dx sub sp,10h ; reserve room for file name push ss ; set es=ss pop es mov di,sp call FCBCopyName push es ; set ds=es pop ds mov dx,di mov ax,3D02h ; open file for R/W pushf cli call cs:[Int21h@Old] jnc FCBCreate@Blank add sp,10h pop dx pop ds cmp ax,5h ; access denied ? jne FCBCreate@Create stc jmp short FCBCreate@Ret FCBCreate@Blank: add sp,10h mov bx,ax call BlankFile pop dx pop ds pushf mov ah,3Eh ; close file pushf cli call cs:[Int21h@Old] popf jc FCBCreate@Ret FCBCreate@Create: mov ah,16h ; create via FCB pushf cli call cs:[Int21h@Old] add al,1h ; error occurred ? FCBCreate@Ret: ret FCBCreate ENDP FCBCopyName PROC NEAR ; Comment: Get file name from unopened FCB and copy it to buffer ; Expects: ds:[dx] - unopened FCB, ; es:[di] - buffer for file name copy (15 bytes) ; Returns: es:[di] - buffer with file name ; Uses: ax,cx,si push di cld mov si,dx lodsb ; check drive ID test al,al ; current drive ? jz FCBCopyName@CurDrive add al,'A'-1h mov ah,':' stosw FCBCopyName@CurDrive: movsw ; copy blank-padded name movsw movsw movsw std ; delete blanks dec di mov cx,8h mov al,' ' repe scasb cld inc di inc di mov al,'.' stosb movsb ; copy blank-padded extension movsw std ; delete blanks dec di mov cx,3h mov al,' ' repe scasb mov byte ptr es:[di+2h],0 ; add zero byte pop di ret FCBCopyName ENDP CreateFile PROC NEAR ; Comment: New "Create file" DOS function (3Ch) ; Expects: ds:[dx] - ASCIIZ file specification, cx - file attribute ; Returns: CF - error flag, ax - error code if CF=1, ; ax - file handle if CF=0 ; Uses: ax,bx,cx,dx,si,di,ds mov ax,3D02h ; open file for R/W pushf cli call cs:[Int21h@Old] jnc CreateFile@Blank cmp ax,5h ; access denied ? je CreateFile@Err mov ah,3Ch ; create file pushf cli call cs:[Int21h@Old] jmp short CreateFile@Ret CreateFile@Blank: mov bx,ax call BlankFile jnc CreateFile@Ret push ax mov ah,3Eh ; close file pushf cli call cs:[Int21h@Old] pop ax CreateFile@Err: stc CreateFile@Ret: ret CreateFile ENDP DeleteFile PROC NEAR ; Comment: New "Delete file (UNLINK)" DOS function (41h) ; Expects: ds:[dx] - ASCIIZ file specification ; Returns: CF - error flag, ax - error code if CF=1 ; Uses: ax,bx,cx,si,di,es mov ax,3D02h ; open file for R/W pushf cli call cs:[Int21h@Old] jc DeleteFile@Ret push ds push dx mov bx,ax call BlankFile pop dx pop ds push ax pushf mov ah,3Eh ; close file pushf cli call cs:[Int21h@Old] popf pop ax jc DeleteFile@Ret call Remove ; remove file DeleteFile@Ret: ret DeleteFile ENDP MoveFile PROC NEAR ; Comment: New "Move file (RENAME)" DOS function (56h) ; Expects: ds:[dx] - ASCIIZ file specification, ; es:[di] - ASCIIZ new file name ; Returns: CF - error flag, ax - error code if CF=1 ; Uses: ax,bx,cx,si push es push di call GetNameLen pop di pop es jc MoveFile@NoRename mov bx,sp ; reserve room for file name copy sub sp,ax mov ax,sp push es push di push ss ; set es=ss pop es mov di,ax call PrepareName mov ah,56h ; rename file or directory to temp name pushf cli call cs:[Int21h@Old] jc MoveFile@RenameErr mov cx,ds mov si,dx push es ; set ds:[dx] - ASCIIZ temp file name pop ds mov dx,di pop di pop es mov ah,56h ; rename (move) file or directory pushf cli call cs:[Int21h@Old] jnc MoveFile@Restore push es push di push ax ; preserve error code mov di,si mov es,cx mov ah,56h ; restore original file or directory name pushf cli call cs:[Int21h@Old] pop ax mov dx,si ; restore ds:[dx] - ASCIIZ file specification mov ds,cx MoveFile@RenameErr: pop di pop es mov sp,bx MoveFile@NoRename: mov ah,56h ; rename (move) file or directory pushf cli call cs:[Int21h@Old] jmp short MoveFile@Ret MoveFile@Restore: mov sp,bx mov dx,si ; restore ds:[dx] - ASCIIZ file specification mov ds,cx MoveFile@Ret: ret MoveFile ENDP SmartOpen PROC NEAR ; Comment: New "Smart open/create file" DOS function (6C00h) ; Expects: bx - open mode flags, cx - file attribute, dx - action, ; ds:[si] - ASCIIZ file specification ; Returns: CF - error flag, ax - error code if CF=1, ; ax - file handle if CF=0, cx - action taken (1,2,3) ; Uses: ax,cx,dx,di,ds and dl,0F0h ; if file exists, open it or dl,1h test bl,3h jnz SmartOpen@Open or bl,2h ; open file for R/W SmartOpen@Open: mov ax,6C00h pushf cli call cs:[Int21h@Old] jc SmartOpen@Ret cmp cx,2h ; file was created ? jae SmartOpen@Ret ; CF=0 mov bx,ax call BlankFile mov cx,3h ; file was replaced jnc SmartOpen@Ret push ax mov ah,3Eh ; close file on error pushf cli call cs:[Int21h@Old] pop ax stc ; set error flag SmartOpen@Ret: ret SmartOpen ENDP BlankFile PROC NEAR ; Comment: Blank file (write blank values to file) ; Expects: bx - file handle ; Returns: CF - error flag, ax - error code if CF=1, ; ax - file handle if CF=0 ; Uses: ax,cx,dx,si,di,ds mov ax,4201h ; get current file pointer position sub cx,cx sub dx,dx pushf cli call cs:[Int21h@Old] jc BlankFile@TruncCRef mov si,ax mov di,dx mov ax,4202h ; get file length sub cx,cx sub dx,dx pushf cli call cs:[Int21h@Old] jc BlankFile@RetCRef mov cx,di mov di,dx mov dx,si add ax,1FFh ; set sector alignment and ax,0FE00h xchg ax,si pushf ; set current file pointer position mov ax,4200h cli call cs:[Int21h@Old] jc BlankFile@RetCRef sub si,ax ; get tail length sbb di,dx jb BlankFile@TruncCRef jnz BlankFile@Prepare test si,si jnz BlankFile@Prepare BlankFile@TruncCRef: jmp BlankFile@Trunc BlankFile@RetCRef: jmp BlankFile@Ret BlankFile@Prepare: push dx ; store current position push ax push es ; load segment and length of working buffer pop ds les cx,dword ptr cs:[TSR@StackTop-0Eh] shl cx,1h shl cx,1h shl cx,1h shl cx,1h test di,di ; buffer not too big ? jnz BlankFile@SkipAdjust cmp cx,si jb BlankFile@SkipAdjust mov cx,si ; adjust buffer size inc cx BlankFile@SkipAdjust: mov ax,NOT TSR@BlankWord shr cx,1h mov dx,di cld ; fill in work buffer sub di,di rep stosw push ds push es pop ds pop es xchg cx,di xchg dx,di push si push di test di,di ; get amount of bytes to write push cx jnz BlankFile@LoopOne BlankFile@CheckOne: cmp si,cx jae BlankFile@LoopOne mov cx,si BlankFile@LoopOne: pushf ; write portion of blanks mov ah,40h cli call cs:[Int21h@Old] jc BlankFile@ErrRestOne ; error ? cmp ax,cx jb BlankFile@ErrRestOne sub si,cx ; continue ? sbb di,0 ja BlankFile@LoopOne test si,si jnz BlankFile@CheckOne BlankFile@ErrRestOne: pop ax pop di pop si pop dx ; restore current file pointer position pop cx push ax pushf mov ax,4200h cli call cs:[Int21h@Old] pop cx jc BlankFile@Ret push dx ; store current position push ax mov ax,TSR@BlankWord cld ; fill in work buffer push ds push es pop ds pop es mov dx,di shr cx,1h sub di,di rep stosw push ds push es pop ds pop es xchg cx,di xchg dx,di test di,di ; get amount of bytes to write jnz BlankFile@LoopTwo BlankFile@CheckTwo: cmp si,cx jae BlankFile@LoopTwo mov cx,si BlankFile@LoopTwo: pushf ; write portion of blanks mov ah,40h cli call cs:[Int21h@Old] jc BlankFile@ErrRestTwo ; error ? cmp ax,cx mov ax,5h ; set "access denied" error code jb BlankFile@ErrRestTwo sub si,cx ; continue ? sbb di,0 ja BlankFile@LoopTwo test si,si jnz BlankFile@CheckTwo sub ax,ax BlankFile@ErrRestTwo: pop dx pop cx push ax ; preserve error code pushf ; restore current file pointer position mov ax,4200h cli call cs:[Int21h@Old] pop ax test ax,ax stc jnz BlankFile@Ret BlankFile@Trunc: mov ah,40h ; truncate file sub cx,cx pushf cli call cs:[Int21h@Old] jc BlankFile@Ret mov ax,bx ; return file handle if no error BlankFile@Ret: ret BlankFile ENDP Remove PROC NEAR ; Comment: rename and remove file or directory ; Expects: CF=1 - operation with directory, ; ds:[dx] - source ASCIIZ file (or directory) specification ; Returns: CF - error flag, ax - error code if CF=1 ; Uses: ax,bx,cx,si,di,es rcl si,1h ; preserve operation type call GetNameLen jc Remove@NoRename mov bx,sp ; reserve room for file name copy sub sp,ax push ss ; set es=ss pop es mov di,sp push si call PrepareName pop si mov ah,56h ; rename file or directory pushf cli call cs:[Int21h@Old] jc Remove@RenameErr push ds ; set ds:[dx] - New file name push es ; set es:[di] - Source (old) file name pop ds pop es xchg dx,di rcr si,1h ; is a file ? jnc Remove@Purge mov ah,3Ah ; remove renamed directory pushf cli call cs:[Int21h@Old] jc Remove@Err stc Remove@Purge: call PurgeFile jnc Remove@Restore Remove@Err: push ax ; preserve error code mov ah,56h ; restore source file or directory name pushf cli call cs:[Int21h@Old] pop ax stc ; set error flag Remove@Restore: mov sp,bx push es ; restore ds:[dx] pop ds mov dx,di jmp short Remove@Ret Remove@RenameErr: mov sp,bx Remove@NoRename: rcr si,1h ; is a file ? jnc Remove@Delete mov ah,3Ah ; remove source directory pushf cli call cs:[Int21h@Old] jc Remove@Ret stc Remove@Delete: call PurgeFile Remove@Ret: ret Remove ENDP GetNameLen PROC NEAR ; Comment: Get file name length ; Expects: ds:[dx] - ASCIIZ file specification ; Returns: CF - error flag, cx - file name legth if CF=0, ; ax - required file name buffer length if CF=0 ; Uses: ax,cx,di,es cld push ds ; set es=ds pop es mov di,dx mov cx,60h ; file specification max length mov al,0 ; get specification length repne scasb stc jne GetNameLen@Ret dec di mov cx,di sub cx,dx stc mov ax,cx jz GetNameLen@Ret add ax,0Eh and al,0FEh ; set word alignment, CF=0 GetNameLen@Ret: ret GetNameLen ENDP PrepareName PROC NEAR ; Comment: Copy and prepare file name ; Expects: ds:[dx] - source ASCIIZ file specification, ; cx - specification length, es:[di] - buffer for file specification copy ; Returns: es:[di] - prepared file specification ; Uses: ax,cx,si push es push di push ds ; set es=ds pop es mov si,cx std ; get file path length mov di,dx add di,cx dec di mov al,'\' repne scasb jne PrepareName@DriveID inc cx inc di PrepareName@DriveID: cld ; search drive ID delimiter sub si,cx mov cx,si inc di mov si,di mov al,':' repne scasb jne PrepareName@Copy mov si,di PrepareName@Copy: pop di pop es push di mov cx,si ; copy file path only sub cx,dx mov si,dx rep movsb mov word ptr es:[di],'~' ; add "~" name pop di ret PrepareName ENDP PurgeFile PROC NEAR ; Comment: purge specified file ; Expects: CF=1 - create file, ds:[dx] - ASCIIZ file specification ; Returns: CF - error flag, ax - error code if CF=1 ; Uses: ax,cx mov ax,3D02h ; create new file or open existing file for R/W sbb ah,0 ; create ? mov cx,20h ; set Archive flag pushf cli call cs:[Int21h@Old] jc PurgeFile@Ret push bx push dx mov bx,ax mov ax,5701h ; reset file modification date and time sub cx,cx ; "0:00" time mov dx,21h ; "1/01/1980" date pushf cli call cs:[Int21h@Old] pop dx mov ah,3Eh ; close file pushf cli call cs:[Int21h@Old] pop bx mov ah,41h ; delete file pushf cli call cs:[Int21h@Old] PurgeFile@Ret: ret PurgeFile ENDP ; RAM-resident part messages Int2Fh@IDStr DB 'File and Directory Complete Deletion' DB ' RAM-resident Utility v1.3',0 Int2Fh@IDStrLen= $-Int2Fh@IDStr EVEN TSR@Length= $-Null TSR@DefBuffer LABEL WORD ; default working buffer ; Non-resident part code ASSUME ds:@CODE,es:NOTHING Main PROC NEAR ; Comment: - ; Expects: ds=es=cs ; Returns: - (not applicable) ; Uses: - (all) mov sp,OFFSET StackTop mov ah,40h ; write info to STDOUT sub bx,bx mov dx,OFFSET Int2Fh@IDStr mov cx,Int2Fh@IDStrLen-1h int 21h mov ah,9h ; print CR/LF mov dx,OFFSET Main@MsgCRLF int 21h mov ax,2523h ; set up new int 23h handler ; Ignore CTRL+C (BREAK) mov dx,OFFSET Int23h int 21h mov ax,2524h ; set up new int 24h handler ; Fail operation mov dx,OFFSET Int24hApp int 21h call GetFirstOption cmp al,'?' ; show help information ? mov dx,OFFSET Main@MsgHelp je Main@Print cmp al,'H' je Main@Print cmp al,'h' je Main@Print push ax ; TSR already installed ? call CheckIDNumber pop dx jnc Main@Already call GetIDNumber ; prepare for installation mov dx,OFFSET Main@MsgErr jc Main@Print push ax call FreeEnvSeg pop ax call SetUpTSR mov ah,9h ; print OK message mov dx,OFFSET Main@MsgInst int 21h mov ax,3100h ; terminate but stay resident mov dx,(TSR@Length+0Fh)/10h+TSR@DefBufLen mov byte ptr ds:[TSR@StackTop-1h],0 ; open gates int 21h Main@Already: cmp dl,'U' ; uninstall driver ? je Main@Uninstall cmp dl,'u' mov dx,OFFSET Main@MsgInst jne Main@Print Main@Uninstall: call UnloadTSR mov dx,OFFSET Main@MsgErr jnz Main@Print mov ah,49h ; free RAM-resident part int 21h mov dx,OFFSET Main@MsgUnload Main@Print: mov ah,9h ; print info and exit int 21h mov ax,4C00h int 21h Main ENDP GetFirstOption PROC NEAR ; Comment: get first option argument from CMD line (/X or -X) ; Expects: ds - PSP segment ; Returns: al - option, ds:[si+1h] - option argument ; Uses: si mov si,OFFSET PSP@CmdLine mov al,0 cmp byte ptr ds:[si],2h ; empty command line ? jb GetFirstOption@Ret GetFirstOption@Loop: inc si cmp byte ptr ds:[si],' ' ; whitespace or end of command line ? je GetFirstOption@Loop jb GetFirstOption@Ret cmp byte ptr ds:[si],'/' je GetFirstOption@Get cmp byte ptr ds:[si],'-' jne GetFirstOption@Loop GetFirstOption@Get: inc si mov al,byte ptr ds:[si] GetFirstOption@Ret: ret GetFirstOption ENDP CheckIDNumber PROC NEAR ; Comment: Check whether TSR already installed ; Expects: ds=cs ; Returns: ah - ID number (if CF=0), es - TSR segment (if CF=0), ; CF=1 - not found ; Uses: ax,bx,cx,dx,si,di,es mov bx,0C001h CheckIDNumber@Loop: xor ax,ax mov es,ax mov ah,bh ; ah - checked ID number, al=0 push bx ; preserve bx int 2Fh pop bx cmp ax,bx ; check whether al=1h jne CheckIDNumber@Cont mov ax,es test ax,ax ; check whether es=0 jz CheckIDNumber@Cont cld ; compare ID strings (ds:[si] and es:[di]) mov si,OFFSET Int2Fh@IDStr mov cx,Int2Fh@IDStrLen repe cmpsb je CheckIDNumber@Ret CheckIDNumber@Cont: add bh,1h jnc CheckIDNumber@Loop CheckIDNumber@Ret: mov ax,bx ; ah - ID number ret CheckIDNumber ENDP GetIDNumber PROC NEAR ; Comment: Get available ID number ; Expects: - ; Returns: ah - ID number (if CF=0), CF=1 - not found ; Uses: ax,bx,cx,dx,si,di,es mov bx,0C000h GetIDNumber@Loop: mov ax,bx ; ah - checked ID number, al=0 push bx ; preserve bx int 2Fh pop bx cmp ax,bx ; check whether ID number reserved je GetIDNumber@Ret add bh,1h jnc GetIDNumber@Loop GetIDNumber@Ret: ret GetIDNumber ENDP FreeEnvSeg PROC NEAR ; Comment: Free program environment segment ; Expects: - ; Returns: - ; Uses: ax,bx,es mov ah,51h ; get PSP segment int 21h mov es,bx sub ax,ax xchg ax,es:[PSP@EnvSeg] test ax,ax ; check for presence of PSP jz FreeEnvSeg@Ret mov es,ax mov ah,49h int 21h FreeEnvSeg@Ret: ret FreeEnvSeg ENDP SetUpTSR PROC NEAR ; Comment: initialize variables and set up new interrupt handlers ; Expects: ds=cs, ah - ID number ; Returns: - ; Uses: ax,bx,dx,es mov ds:[Int2Fh@IDNumber],ah ; assign ID number mov ax,3521h ; store old int 21h handler addr int 21h mov word ptr ds:[Int21h@Old],bx mov word ptr ds:[Int21h@Old+2h],es mov ax,352Fh ; store old int 2Fh handler addr int 21h mov word ptr ds:[Int2Fh@Old],bx mov word ptr ds:[Int2Fh@Old+2h],es mov ax,2521h ; set up new int 21h handler mov dx,OFFSET Int21h int 21h mov ax,252Fh ; set up new int 2Fh handler mov dx,OFFSET Int2Fh int 21h ret SetUpTSR ENDP UnloadTSR PROC NEAR ; Comment: unload TSR from memory (if possible) ; Expects: es - TSR segment ; Returns: ZF=0 - TSR cannot be unloaded now ; Uses: ax,bx,dx push es mov ax,3521h ; check current int 21h handler int 21h mov ax,es cmp bx,OFFSET Int21h pop es jne UnloadTSR@Ret mov bx,es cmp ax,bx jne UnloadTSR@Ret push es mov ax,352Fh ; check current int 2Fh handler int 21h mov ax,es cmp bx,OFFSET Int2Fh pop es jne UnloadTSR@Ret mov bx,es cmp ax,bx jne UnloadTSR@Ret push ds mov ax,2521h ; restore old int 21h handler lds dx,es:[Int21h@Old] int 21h mov ax,252Fh ; restore old int 2Fh handler lds dx,es:[Int2Fh@Old] int 21h pop ds UnloadTSR@Ret: ret UnloadTSR ENDP ; Program messages Main@MsgInst DB 'Driver installed.' Main@MsgCRLF DB 0Dh,0Ah,'$' Main@MsgUnload DB 'Driver removed from memory.',0Dh,0Ah,'$' Main@MsgErr DB 'Error!',0Dh,0Ah,'$' Main@MsgHelp DB 0Dh,0Ah DB ' This is a resident program that handles "Delete","RMDIR",',0Dh,0Ah DB '"Move","Create" and "Truncate" DOS functions in such a way',0Dh,0Ah DB 'that all the deleted data will be unrecoverable.',0Dh,0Ah DB 0Dh,0Ah,'$' ; Program stack EVEN DB 100h DUP(?) StackTop LABEL WORD ; End of program END Start