递归列出目录内容,并检查文件是否是目录

时间:2011-11-19 13:48:22

标签: linux assembly x86 nasm

我正在努力学习集会,所以如果我的问题是基本的话,请耐心等待。

以下代码扫描目录并打印出其中的所有文件和目录,但以点开头的那些文件和目录除外。它似乎工作正常。

但是,一旦我取消注释call scandir行以打开递归,它就会打印出一长串重复的文件名(详见下文)。

另外,我想要一种测试文件是否是目录的方法。我该怎么办?据我所知,这不是问题,现在,因为如果它不是一个目录,那么对scandir的调用只会在没有做任何事情的情况下返回,但是检查可能会在以后变得很重要(这似乎是件好事)反正)。

[SECTION .data]
DirName     db 'test', 0

[SECTION .bss]

[SECTION .text]

extern puts                  ; Externals from glibc standard C library
extern opendir               ; Externals from dirent.h
extern closedir
extern readdir

global main


scandir:
    pushad                  ; Save caller's registers

    push eax                ; Directory is passed in eax
    call opendir            ; Open directory
    add esp, 4
    cmp eax, 0              ; opendir returns 0 on failure
    je .done

    mov ebx, eax            ; Move directory handle to ebx

    .read:
        push ebx            ; Push directory handle
        call readdir        ; Read a file from directory
        add esp, 4          ; Clean up the stack
        cmp eax, 0          ; readdir returns 0 on failure or when done
        je .close

        add eax, 11         ; File name is offset at 11 bytes

        mov cl, byte [eax]  ; Get first char of filename
        cmp cl, 46          ; Ignore files and dirs which begin with a dot
        je .read            ; (., .., and hidden files)

        ;call scandir       ; Call scandir recursively
                            ; If file is not a dir opendir will simply fail

        push eax
        call puts
        add esp, 4

        jmp .read

    .close:
        push ebx                ; Close directory
        call closedir
        add esp, 4
        jmp .done

    .done:
        popad                   ; Restore caller's registers
        ret

main:
    push ebp                ; Set up stack frame for debugger
    mov ebp, esp
    push ebx                ; Must preserve ebp, ebx, esi and edi
    push esi
    push edi
    ; start

    mov eax, DirName
    call scandir

    ; end
    pop edi                 ; Restore saved registers
    pop esi
    pop ebx
    mov esp, ebp            ; Destroy stack frame
    pop ebp
    ret

测试目录的目录结构如下:

bar [directory]
    bas.c
test1.c
test2.c
test3.c
foo.txt
test

没有递归,它会打印出测试目录中的文件,但是通过递归,它似乎打印出以下内容:

test1.c
bar
test3.c
[repeat 3 lines ~1000 times]
test
foo.txt
test2.c
[repeat 3 lines ~1000 times]

编辑:我认为现在大部分都可以使用了,除了它似乎最初跳回到'test'下面的目录,导致它列出那里的文件和'test'中的文件两次

[SECTION .data]
ParentDir   db '..', 0
CurrentDir  db '.', 0
DirName     db 'test', 0

[SECTION .bss]

[SECTION .text]
extern puts                 ; Externals from glibc standard C library
extern opendir              ; Externals from dirent.h
extern closedir
extern readdir
extern chdir

global main

scandir:
    pushad                  ; Save caller's registers

    push eax                ; Directory is passed in eax
    call opendir            ; Open directory
    add esp, 4
    cmp eax, 0              ; opendir returns 0 on failure
    je .done

    mov ebx, eax            ; Move directory handle to ebx

    .read:
        push ebx            ; Push directory handle
        call readdir        ; Read a file from directory
        add esp, 4          ; Clean up the stack
        cmp eax, 0          ; readdir returns 0 on failure or when done
        je .close

        add eax, 11         ; File name is offset at 11 bytes

        mov cl, byte [eax]  ; Get first char of filename
        cmp cl, 46          ; Ignore files and dirs which begin with a dot
        je .read            ; (., .., and hidden files)

        cmp byte [eax-1], 4
        jne .notdir

        push eax
        call chdir
        add esp, 4
        mov eax, CurrentDir
        call scandir        ; Call scandir recursively
        jmp .read

    .notdir:
        push eax
        call puts
        add esp, 4

        jmp .read

    .close:
        push ebx                ; Close directory
        call closedir
        add esp, 4

        push ParentDir
        call chdir
        add esp, 4

        jmp .done

    .done:
        popad                   ; Restore caller's registers
        ret

main:
    push ebp                ; Set up stack frame for debugger
    mov ebp, esp
    push ebx                ; Must preserve ebp, ebx, esi and edi
    push esi
    push edi
    ; start

    mov eax, DirName
    call scandir

    ; end
    pop edi                 ; Restore saved registers
    pop esi
    pop ebx
    mov esp, ebp            ; Destroy stack frame
    pop ebp
    ret

1 个答案:

答案 0 :(得分:0)

你的代码看起来很好。问题在于readdir返回的路径名,您希望将其用于下一个递归步骤。返回的路径名与当前工作目录无关。

您看到的大量输出是test/目录包含名为test的文件。当您的循环看到该文件名时,您将其传递给opendir,这意味着您只需再次重新打开相同的test/目录,从而导致无限递归,直到您用完文件句柄。

解决此问题的一种方法是在成功chdir之后调用opendir(以及chdirclosedir回到父目录),以便工作目录将总是指向你正在检查的那个。


  

另外,我想要一种测试文件是否是目录的方法。

readdir返回的dirent结构有d_type成员(偏移10),您可以查看:

...
call readdir
...
cmp byte [eax+10], 4    ; DT_DIR = 4
jne is_not_directory