I am working on a script 'ATTRIB16.G4B' to read-out file/ directory attributes.
With FAT16 thing are going very well, to include FAT32 I expect no real problems, but with FAT12 I need help how to read-out a 12-bits FAT lookup-table, so reading the FAT on a FAT12 file-system.
In fact I started with FAT12, but if a sub-directory is bigger than one cluster, I run into problems, so I 'did' FAT16 first.
Some pictures to introduce the project, including some echo and cat-lines to see what is actually going on.
Finding file/ directory attributes of files in the root-directory is relatively easy, same in sub-directories if they do not exceed the size on one cluster. Also the Volume label attribute can be read-out, but only if in uppercase.
First I had problems, because I wrote the label lowercase with Grub4dos Vol, not a good idea on FAT file-systems (and against the specs if I am right).
A very big directory (more than one cluster) was hard to find on my test system, but I was lucky the Windows 98SE Windows\System-directory was big enough. See next print-screen...
On FAT16 finding the next cluster in the FAT lookup-table was relatively easy, only relative addressing after copying with dd the whole FAT to memory was more complicated. I know no way to read hex-values directly from a file (except getting them from the textual output of cat --hex but that is rather inconvenient, also because of the Little-Endian 'thing').
This is my current script, still relatively small - without extra functionality.
My basic idea is quite simple.
1) Get needed file-system infomation [sub-routines :filesystem and :bootblockbase]
2) Finding the cluster-numbers of all sub-directories in the PATH [sub-routines :findpath + :finddirectory, with help of :catfile + :spread to get the right name-formatting]
3) Finding attributes of searched file or directory (or volume label) and converting the hex-value to binary [sub-routines :findfile and :hex2bin + :cnvhex2bin]
BTW if searched file or directory is in the root, step 2) is skipped.
!BAT #ATTRIB16.G4B v0.1 (20220126), by deomsh. #Function: Find FAT16 file/ directory attributes #Mandatory Arguments: FILE #Optional Arguments: #Remarks: setlocal && set * debug msg=0 #echo %* set /A mdbase=0x20000 &; set /A basemem=%mdbase% * 0x200 set root=%@root% && root %~d1 > nul && set /a devsect=*0x82B0 &; root=%root% > nul && set root= set /A devsize=%devsect% * 0x200 set device=%~d1 set path=%~p1 set file=%~f1 if not exist %file% && echo %file% is not an existing file, can be a directory name... set name=%~n1 set ext=%~x1 call :filesystem "%device%" &; if not /1 %filesys%==fat16 && echo %filesys% not supported && goto :eof echo File System=%filesys% call :bootblockbase "%device%" pause && clear call :findpath "%path%" &; if %clusnum%==0 && echo %file% not found && goto :eof if not "%path%"=="/" && if not exist entry && echo %file% not found && goto :eof if exist bytedone && call :findfile "%name%%%ext%" ! echo directory entry %file% not found && goto :eof if not exist attrib && echo Attributes not found && echo && set && goto :eof ! call :hex2bin "%attrib%" echo && echo attrib=%attrib% && echo Read-only=%bit0% System=%bit1% Hidden=%bit2% Volume label=%bit3% Directory=%bit4% Archive=%bit5% echo && echo %0 %1 && echo #todo#call :attributes debug msg=3 goto :eof :findfile setlocal call :catfile "%~1" :findfileloop set entry= && set success= cat --skip=%bytedone% --locate=%ENTRY% --length=%dirlen% %device%%0+%devsect% > nul &; set /A entry=%?% echo clusnum=%clusnum% bytedone=%bytedone% entry=%entry% if exist entry && cat --hex --skip=%entry% --length=0x20 %device%%0+%devsect% if exist entry && echo -n > (md)%mdbase%+1 && raw dd if=%device%%0+%devsect% of=(md)%mdbase%+1 bs=1 count=0x20 skip=%entry% > nul && set success=Y if %success%==Y && set /A address=%basemem%+0xB &; read %address% &; set /A attrib=%@retval%&0xff if not exist entry && if /i %filesys%==fat16 && if %bytedone%>=%dataarea% && if not %bytedone%>=%devsize% && if exist clusnum && call :lookupFAT "%clusnum%" &; if %clusnum%>=2 && set /A bytedone=%clusnum% - 0x2 * %clussize% + %dataarea% && set /A dirlen=%clussize% && goto :findfileloop endlocal && set attrib=%attrib% && set entry=%entry% goto :eof :findpath setlocal set "path=%~1" set bytedone=%root% && set /A dirlen=%dataarea%-%root% if "%path%"=="/" && endlocal && set bytedone=%bytedone% && set dirlen=%dirlen% && goto :eof echo "%path%" > (md)%mdbase%+1 #cat --locate=/ (md)%mdbase%+1 > nul &; set /a numslash=%@retval% > nul #if %numslash%>=21 && endlocal && set "message=No new path made: path too deep" && set tryagain=Y && goto :eof cat --locate=/ --replace=\x20 (md)%mdbase%+1 > nul cat --locate=\x22 --replace=\x20 (md)%mdbase%+1 > nul set clusnum=0 && cat (md)%mdbase%+1 | call :finddirectory endlocal && set clusnum=%clusnum% && set bytedone=%bytedone% && set dirlen=%dirlen% && set entry=%entry% goto :eof :finddirectory if "%1"=="" && goto :eof set /A address=%basemem%+0x1A set ENTRY= && call :catfile "%~1" :finddirectoryloop set entry= && set success= cat --skip=%bytedone% --locate=%ENTRY% --length=%dirlen% %device%%0+%devsect% > nul &; set /A entry=%?% echo clusnum=%clusnum% bytedone=%bytedone% entry=%entry% if exist entry && cat --hex --skip=%entry% --length=0x20 %device%%0+%devsect% if exist entry && echo -n > (md)%mdbase%+%clussect% && raw dd if=%device%%0+%devsect% of=(md)%mdbase%+1 bs=1 count=0x20 skip=%entry% > nul && set success=Y if %success%==Y && read %address% &; set /A clusnum=%@retval%&0xffff if not exist entry && if not %clusnum%==0 && if /i %filesys%==fat16 && if %bytedone%>=%dataarea% && if not %bytedone%>=%devsize% && if exist clusnum && call :lookupFAT "%clusnum%" &; if %clusnum%>=2 && set /A bytedone=%clusnum% - 0x2 * %clussize% + %dataarea% && set /A dirlen=%clussize% && goto :finddirectoryloop set /A bytedone=%clusnum% - 0x2 * %clussize% + %dataarea% && set /A dirlen=%clussize% echo #echo bytedone=%bytedone% dirlen=%dirlen% entry=%entry% clusnum=%clusnum% if not %clusnum%==0 && shift ! goto :eof goto :finddirectory :lookupFAT setlocal &; set clusnum=%~1 && set FATbytes=2 set /A mdfat=%mdbase%+1 &; set /A fatmem=%mdfat% * 0x200 && set /A fatlen=%secpfat% * 0x200 call Fn.24 %fatmem% 0x00 %fatlen% raw dd if=%device%%0+%devsect% of=(md)%mdfat%+%secpfat% bs=1 count=%fatlen% skip=%fat1% > nul && set success=Y if %success%==Y && echo -n in :lookupFAT FATbytes=%FATbytes% fatmem=%fatmem% && set /A address=%clusnum% * %FATbytes% + %fatmem% &; echo -n -e \x20address=%address% && read %address% &; set /A nextclus=%@retval%&0xffff &; echo -e \x20nextclus=%nextclus% && if not %nextclus%>=0xffff && set clusnum=%nextclus% ! set clusnum=0 #clusnum=%clusnum% echo #fatlen=%fatlen% secpfat=%secpfat% success=%success% && pause && set clussect=%secpclus% endlocal && set clusnum=%clusnum% goto :eof :catfile setlocal && set * &; set name=%~1 && set ext=%~2 set /u NAME=%~n1 &; set /a namelen=%@retval% &; if %namelen%<=8 && call :spread "NAME" "%namelen%" &; set NAME=%NAME%%%spread% && set spread= set /u EXT=%~x1 ;; set EXT=%EXT:~1% ;; set /a extlen=%@retval% &; if exist EXT && if %extlen%<=3 && call :spread "EXT" "%extlen%" &; set EXT=%EXT%%%spread% && set spread= if exist EXT && set ENTRY=%NAME%%%EXT% ! set ENTRY=%NAME%\x20\x20\x20 #echo ENTRY=%ENTRY% endlocal && set ENTRY=%ENTRY% goto :eof :filesystem setlocal && set * && set mdbase=%mdbase% set device=%~1 echo -n > (md)%mdbase%+1 errorcheck off debug msg=1 if exist device && vol %device% > (md)%mdbase%+1 debug msg=0 if exist device && set filesys=fat12 &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=fat16 &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=fat32 &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=ntfs &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=exfat &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=ext2fs &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=iso9660 &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=reiserfs &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=udf &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=initrdfs &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=minix &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=vstafs &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=jfs &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=xfs &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=ufs2 &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=ffs &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=fb &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=tftp &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=pxe &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=ipxe &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys= endlocal && set filesys=%filesys% goto :eof :hex2bin #-#+ call :hex2bin <hexa-decimal number> #-# Mandatory Argument: 0x before hex number = choice #-# Remarks: max 16-bit. setlocal && set * set ARG2=%~1 if not %ARG2:~0,2%==0x && set hex=0x%ARG2:~2% ! set hex=%ARG2% if %hex%>=16 && set /A nibble0=0x%hex:~-1,1% && set /A nibble1=%hex:~0,3% ! set /A nibble0=0x%hex:~-1,1% && set /A nibble1=0x0 call :cnvhex2bin "%nibble0%" &; set bit0=%bitA% && set bit1=%bitB% && set bit2=%bitC% && set bit3=%bitD% && set bitA= && set bitB= && set bitC= && set bitD= call :cnvhex2bin "%nibble1%" &; set bit4=%bitA% && set bit5=%bitB% && set bit6=%bitC% && set bit7=%bitD% && set bitA= && set bitB= && set bitC= && set bitD= endlocal && set bit0=%bit0% && set bit1=%bit1% && set bit2=%bit2% && set bit3=%bit3% && set bit4=%bit4% && set bit5=%bit5% && set bit6=%bit6% && set bit7=%bit7% goto :eof :cnvhex2bin setlocal set nibble=%~1 if %nibble%==0 && set bit0=0 && set bit1=0 && set bit2=0 && set bit3=0 if %nibble%==1 && set bit0=1 && set bit1=0 && set bit2=0 && set bit3=0 if %nibble%==2 && set bit0=0 && set bit1=1 && set bit2=0 && set bit3=0 if %nibble%==3 && set bit0=1 && set bit1=1 && set bit2=0 && set bit3=0 if %nibble%==4 && set bit0=0 && set bit1=0 && set bit2=1 && set bit3=0 if %nibble%==5 && set bit0=1 && set bit1=0 && set bit2=1 && set bit3=0 if %nibble%==6 && set bit0=0 && set bit1=1 && set bit2=1 && set bit3=0 if %nibble%==7 && set bit0=1 && set bit1=1 && set bit2=1 && set bit3=0 if %nibble%==8 && set bit0=0 && set bit1=0 && set bit2=0 && set bit3=1 if %nibble%==9 && set bit0=1 && set bit1=0 && set bit2=0 && set bit3=1 if %nibble%==10 && set bit0=0 && set bit1=1 && set bit2=0 && set bit3=1 if %nibble%==11 && set bit0=1 && set bit1=1 && set bit2=0 && set bit3=1 if %nibble%==12 && set bit0=0 && set bit1=0 && set bit2=1 && set bit3=1 if %nibble%==13 && set bit0=1 && set bit1=0 && set bit2=1 && set bit3=1 if %nibble%==14 && set bit0=0 && set bit1=1 && set bit2=1 && set bit3=1 if %nibble%==15 && set bit0=1 && set bit1=1 && set bit2=1 && set bit3=1 set bitA=%bit0% && set bitB=%bit1% && set bitC=%bit2% && set bitD=%bit3% endlocal && set bitA=%bitA% && set bitB=%bitB% && set bitC=%bitC% && set bitD=%bitD% goto :eof :bootblockbase setlocal && set * && set mdbase=%mdbase% set device=%~1 echo -n > (md)%mdbase%+1 dd if=%device%%0+1 of=(md)%mdbase%+1 > nul set /A base=%mdbase% * 0x200 set /A address=%base%+0xB read %address% > nul &; set /A bytepsec=%@retval%&0xffff echo bytepsec=%bytepsec% set /A address=%base%+0xD read %address% > nul ;; set /A secpclus=%@retval%&0xff echo secpclus=%secpclus% set /A address=%base%+0xE read %address% > nul ;; set /A reserved=%@retval%&0xffff echo reserved=%reserved% set /A address=%base%+0x10 read %address% > nul ;; set /A numfats=%@retval%&0xff echo numfats=%numfats% set /A address=%base%+0x11 read %address% > nul ;; set /A rootentr=%@retval%&0xffff echo rootentr=%rootentr% set /A address=%base%+0x13 read %address% > nul ;; set /A totsect=%@retval%&0xffff echo totsect=%totsect% set /A address=%base%+0x15 read %address% > nul ;; set /A mediabyt=%@retval%&0xff echo mediabyt=%mediabyt% set /A address=%base%+0x16 read %address% > nul ;; set /A secpfat=%@retval%&0xffff echo secpfat=%secpfat% set /A address=%base%+0x18 read %address% > nul ;; set /A secptrac=%@retval%&0xffff echo secptrac=%secptrac% set /A address=%base%+0x1A read %address% > nul ;; set /A numheads=%@retval%&0xffff echo numheads=%numheads% set /A address=%base%+0x1C read %address% > nul ;; set /A hiddsect=%@retval%&0xffffffff echo hiddsect=%hiddsect% if %totsect%==0 && set /A address=%base%+0x20 if %totsect%==0 && read %address% > nul ;; set /A totsect=%@retval%&0xffffffff echo totsect=%totsect% set /A address=%base%+0x24 read %address% > nul ;; set /A drivenum=%@retval%&0xff echo drivenum=%drivenum% set /A rootbyte=%rootentr% * 32 set /A fatbyte=%numfats% * %secpfat% * %bytepsec% set /A rsrvbyte=%reserved% * %bytepsec% set /A root=%fatbyte% + %rsrvbyte% set /A dataarea=%rootbyte% + %fatbyte% + %rsrvbyte% set /A clussize=%secpclus% * %bytepsec% set /A fat1=%rsrvbyte% set /A fat2=%fatbyte% / 2 + %rsrvbyte% endlocal && set dataarea=%dataarea% && set clussize=%clussize% && set root=%root% && set fat1=%fat1% && set fat2=%fat2% && set secpfat=%secpfat% && set hiddsect=%hiddsect% && set totsect=%totsect% && set secpclus=%secpclus% goto :eof :spread setlocal && set * set "message=%~1" set /a len=%~2 ;; if not exist len && set len=0 if %message%==NAME && set /a spaces=8-%len% && set "char=\x20" if %message%==EXT && set /a spaces=3-%len% && set "char=\x20" set c=1 :spreadloop if %c%<=%spaces% && set "spread=%spread%%%char%" && set /a c=%c%+1 && goto :spreadloop endlocal && set "spread=%spread%" goto :eof #:volumesize #:NameOfFunction setlocal && set * && set mdbase=%mdbase% set root=%@root% && root %~1 > nul && set /a devsect=*0x82B0 &; root=%root% > nul endlocal && set devsect=%devsect% goto :eof :cntvars #-#+ call :cntvars <cntvars> <basemem> <sectors> [<subroutine>] (max 60 sectors, if needed) #echo %~0 %* echo -n > (md)%~2+%~3 set > (md)%~2+%~3 setlocal && set * echo -n && call Fn.4 ;; set /A r=%@retval% > nul ;; set /a y=%r%>>8 > nul ;; set /a x=%r%&0xFF > nul cat --locate=\x0A (md)%~2+%~3 > nul ;; set /a result=%@retval% > nul echo -n > (md)%~2+%~3 :LoopCntVars set ARG=%~4 &; if not "%ARG%"=="" &; if %ARG:~0,1%==: && set ARG4=%~4 ! shift && goto :LoopCntVars if exist ARG4 && set "message=Number of variables=%result% in %ARG4%" ! set "message=Number of variables=%result%" set "message=%message%" ;; set msglen=%@retval% > nul ;; set /a h=79-%msglen% > nul #set ARG4=%ARG4% ;; set ARG4len=%@retval% > nul ;; set /a h=79-23-4-%ARG4len% > nul call Fn.5 %h% 24 || echo -n $[0x0F]%message% #if exist ARG4 && call Fn.5 %h% 24 || echo -n $[0x0F]Number of variables=%result% in %ARG4% #if not exist ARG4 && call Fn.5 56 24 || echo -n $[0x0F]Number of variables=%result% #if exist ARG4 && echo -n Number of variables=%result% in %ARG4% #if not exist ARG4 && echo -n Number of variables=%result% call Fn.5 %x% %y% #set endlocal #endlocal && set result=%result% goto :eof
Only if a searched entry is not found in the first cluster of a sub-directory, there is a call to a sub-routine to read-out the FAT-table [subroutine :lookupFAT].
:lookupFAT setlocal &; set clusnum=%~1 && set FATbytes=2 set /A mdfat=%mdbase%+1 &; set /A fatmem=%mdfat% * 0x200 && set /A fatlen=%secpfat% * 0x200 call Fn.24 %fatmem% 0x00 %fatlen% raw dd if=%device%%0+%devsect% of=(md)%mdfat%+%secpfat% bs=1 count=%fatlen% skip=%fat1% > nul && set success=Y if %success%==Y && set /A address=%clusnum% * %FATbytes% + %fatmem% &; read %address% &; set /A nextclus=%@retval%&0xffff &; if not %nextclus%>=0xffff && set clusnum=%nextclus% ! set clusnum=0 endlocal && set clusnum=%clusnum% goto :eof
This is the whole sub-routine which seems to work on FAT16 (here without echoos):Functions:
1) Copy entire (first) FAT to memory with dd
2) Setting the right memory address to read-out the FAT-entry that corresponds with the last cluster-number where searching was not succesfull, and then read the hex-value with Grub4dos read
3) Evaluating if this cluster should be searched and continue. Or if it was already the last cluster, the file/ directory is set to 'not found'
But how to manage this on FAT12, with 12-bits values instead of 16-bits is not clear to me.
How can I change the bold-written essentail parts of my central script-line to read-out FAT12?
if %success%==Y && set /A address=%clusnum% * %FATbytes% + %fatmem% &; read %address% &; set /A nextclus=%@retval%&0xffff &; if not %nextclus%>=0xffff && set clusnum=%nextclus% ! set clusnum=0
Any help would be greatly appreciated!