;name: dirinfo.asm
;
;description: displays directory entries information of the current working directory
;
;build: nasm -felf64 -Fdwarf -g dirinfo.asm -o dirinfo.o
; ld -g -melf_x86_64 dirinfo.o -o dirinfo
bits 64
%include "unistd.inc"
%include "sys/dirent.inc"
%include "sys/stat.inc"
section .bss
buffer: resb 1024
.len: equ $-buffer
section .data
DIRENT64 dirent
path: db "."
fd: dq 0
nread: dq 0
spacer:
.7: db " " ;each number indicates how many spaces
.6: db " " ;will be used
.5: db " "
.4: db " "
.3: db " "
.2: db " "
.1: db " "
db 0
col: db "|",0
line: times 105 db "-"
db 10,0
tableheader: times 5 db " "
db "inode"
times 8 db " "
db "|"
times 3 db " "
db "next entry"
times 5 db " "
db "|"
times 2 db " "
db "record length"
times 2 db " "
db "|"
times 7 db " "
db "filetype"
times 13 db " "
db "|"
times 2 db " "
db "filename"
times 2 db " "
db 10,0
totallength: times 24 db " "
db "Total length |"
times 2 db " ",0
section .text
global _start
_start:
syscall open,path,O_RDONLY | O_DIRECTORY ;open path in readonly and as directory otherwise fail
and rax,rax
js Exit ;there was an error, just exit
mov qword[fd],rax ;save filedescriptor
syscall getdents64,qword[fd],buffer,buffer.len
and rax,rax
jle Close ;if RAX = 0 : no entries, if RAX < 0 : error
mov qword[nread],rax
mov rsi,line
call String.ToSTDOUT
mov rsi,tableheader
call String.ToSTDOUT
mov rsi,line
call String.ToSTDOUT
xor rdx,rdx ;position in buffer
.repeat:
mov rbx,buffer ;buffer start
add rbx,rdx ;buffer + position = entry
; next entry
xor rcx,rcx ;length of entry
mov rsi,rbx ;this entry
add rbx,16 ;address of length of entry
mov cx,word[rbx] ;put in rcx
mov rdi,dirent ;dirent structure pointer in RDI
cld
rep movsb ;copy RCX bytes from RSI to RDI (into structure)
; read the entry record
push rdx
mov rsi,spacer.1
call String.ToSTDOUT
mov rax,qword[dirent.d_ino]
call Register.64bitsToHex
mov rsi,rax ;pointer to buffer in RSI
call String.ToSTDOUT
mov rsi,spacer.1
call String.ToSTDOUT
mov rsi,col
call String.ToSTDOUT
mov rsi,spacer.1
call String.ToSTDOUT
mov rax,qword[dirent.d_off]
call Register.64bitsToHex
mov rsi,rax
call String.ToSTDOUT
mov rsi,spacer.1
call String.ToSTDOUT
mov rsi,col
call String.ToSTDOUT
mov rsi,spacer.6
call String.ToSTDOUT
mov ax,word[dirent.d_reclen]
call Register.16bitsToHex
mov rsi,rax
call String.ToSTDOUT
mov rsi,spacer.7
call String.ToSTDOUT
mov rsi,col
call String.ToSTDOUT
mov rsi,spacer.2
call String.ToSTDOUT
mov al,byte[dirent.d_type]
call Register.8bitsToHex
mov rsi,rax
call String.ToSTDOUT
mov rsi,spacer.4
call String.ToSTDOUT
mov al,byte[dirent.d_type]
call Entry.GetType
mov rsi,qword[rax]
call String.ToSTDOUT
mov rsi,spacer.2
call String.ToSTDOUT
mov rsi,col
call String.ToSTDOUT
mov rsi,spacer.2
call String.ToSTDOUT
mov rsi,dirent.d_name ;name of entry
call String.ToSTDOUT
mov al,0x0A
call Character.ToSTDOUT
pop rdx
; prepare to read next entry
add dx,word[dirent.d_reclen]
mov rax,qword[nread]
cmp rdx,rax
jl .repeat
mov rsi,line
call String.ToSTDOUT
mov rsi,totallength
call String.ToSTDOUT
mov rax,qword[nread]
call Register.64bitsToHex
mov rsi, rax
call String.ToSTDOUT
mov al,10 ;end of line
call Character.ToSTDOUT
Close:
syscall close,fd ;close file descriptor
Exit:
syscall exit,0
Entry:
.GetType:
section .data
;keep all strings the same size to display them neatly in the table.
FileType:
.Unknown: db "unknown ",0
.Fifo: db "named pipe ",0
.Character: db "character device ",0
.Directory: db "directory ",0
.Block: db "block device ",0
.Regular: db "regular file ",0
.Link: db "symbolic link ",0
.Socket: db "unix domain socket",0
.Without: db "entry without type",0
;the values are:
;decimal binary
;0 0000 ; file type unknown
;1 0001 ; named pipe (fifo)
;2 0010 ; character device
;4 0100 ; directory
;6 0110 ; block device
;8 1000 ; regular file
;10 1010 ; symbolic link
;12 1100 ; UNIX domain socket
;14 1110 ; entry without file type ; undocumented
;To lookup in the table for the appropriate string for each value, we can make use of a table with 15 entries and most of them will be zero (null pointer)
;To shorten the table we can however play with the bit positions. If we change the bits from position in the following manner:
;b3 b2 b1 b0 to b0 b1 b2 b3 then the values for each filetype are as showed:
;
;initial decimal after change decimal
;------- ------- ------------ -------
;0000 0 0000 0 ; file type unknown
;0001 1 1000 8 ; named pipe (fifo)
;0010 2 0100 4 ; character device
;0100 4 0010 2 ; directory
;0110 6 0110 6 ; block device
;1000 8 0001 1 ; regular file
;1010 10 0101 5 ; symbolic link
;1100 12 0011 3 ; UNIX domain socket
;1110 14 0111 7 ; entry without file type ; undocumented
;
;the new values, sorted ascending will give the next table
;
;0000 0 0000 0 ; file type unknown
;1000 8 0001 1 ; regular file
;0100 4 0010 2 ; directory
;1100 12 0011 3 ; UNIX domain socket
;0010 2 0100 4 ; character device
;1010 10 0101 5 ; symbolic link
;0110 6 0110 6 ; block device
;1110 14 0111 7 ; entry without file type ; undocumented
;0001 1 1000 8 ; named pipe (fifo)
;
;now we know how to place the pointers to the strings in our lookup table
.Table: dq .Unknown, .Regular, .Directory, .Socket, .Character, .Link, .Block, .Without, .Fifo
section .text
;The algorithm to change bits to make a mirror of the bits can be found in Hacker's Delight.
;Since we deal with 4 bits in 8 bits registers we only need to process the 4 least significant bits.
and rax,0Fh ;delete unnecessary bits
mov ah,al ;copy value in AH
; algorithm to mirror 4 bits
and ah,0x55
shl ah,1
and al,0xAA
shr al,1
or al,ah
mov ah,al
and ah,0x33
shl ah,2
and al,0xCC
shr al,2
or al,ah ;delete AH
xor ah,ah
;multiply by 8 to calculate 64 bits offset of the string to display.
shl rax,3 ;8 bytes for 64 bit pointer
;added to our base address of the table
add rax,FileType.Table
ret
; routines to display and process register values
Register:
section .bss
.hexbuffer: resb 16
.dummy: resb 1 ;end of string
section .text
.64bitsToHex:
mov rcx,16 ;16 nibbles x 4 bits/nibble = 64 bits
mov rdi,.hexbuffer
jmp .convert
.16bitsToHex:
mov rcx,4
mov rdi,.hexbuffer+24
and rax,0FFFFh
ror rax,16 ;move word bits to upper registry bits
jmp .convert
.8bitsToHex:
mov rcx,2
mov rdi,.hexbuffer+30
and rax,0FFh
ror rax,8 ;move byte bits to upper registry bits
jmp .convert
.convert:
push rdi ;save buffer
;convert RAX to ASCII hexadecimal
mov rdx, rax
xor rax, rax
cld
.repeat:
rol rdx,4 ;start with most significant nibble first
mov al,dl
and al,0Fh
cmp al,9
jbe .toascii
add al,7
.toascii:
add al,"0"
stosb ;store AL in hexbuffer
loop .repeat
pop rax ;start of buffer in RAX
ret
String:
.ToSTDOUT:
cld
.repeat:
lodsb
and al,al
jz .done
push rsi
call Character.ToSTDOUT
pop rsi
jmp .repeat
.done:
ret
Character:
section .bss
.charbuffer: resb 1
section .text
.ToSTDOUT:
mov byte[.charbuffer],al
syscall write,stdout,.charbuffer,1
ret