【问题标题】:Recursively list directory content, plus check if file is directory递归列出目录内容,并检查文件是否为目录
【发布时间】:2011-11-19 13:48:21
【问题描述】:

我正在努力学习汇编,如果我的问题是初级的,请多多包涵

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

但是,一旦我取消注释 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

【问题讨论】:

    标签: linux assembly x86 nasm


    【解决方案1】:

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

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

    解决此问题的一种方法是在成功的opendir 之后调用chdir(以及在closedir 之后将chdir 返回父目录),这样工作目录将始终指向您所在的目录正在检查中。


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

    readdir 返回的 dirent 结构有一个 d_type 成员(在偏移量 10 处),您可以检查:

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

    【讨论】:

    • 谢谢。我想我现在大部分时间都在工作,但它仍然给我带来了一些麻烦。请参阅我上面帖子中的编辑。
    • 仍然是同样的问题 - 您首先列出 test/ 目录,然后将这些路径视为相对于当前工作目录的路径,但它们不是。快速修复(在main 中)是将chdir 转换为DirName 然后mov eax, CurrentDir 在您之前call scandir
    • 啊!或者我只是将chdir 部分移动到scandir 的开头。感谢您的帮助,它现在可以工作了
    • 另一种处理此问题的好方法是使用openat (man7.org/linux/man-pages/man2/open.2.html),您可以在其中使用相对于打开目录 FD 的相对路径,而不必在 chdirchdir 中使用在出去的路上又回来了。这样可以防止将符号链接指向目录 make chdir("foo") / chdir("..") 带您到其他地方。
    • 另外,d_type 是一些文件系统不填写的可选优化。如果要在 FAT、旧 XFS 上工作,可靠的代码必须处理 DT_UNKNOWN ,以及其他一些文件系统。 Checking if a dir. entry returned by readdir is a directory, link or file. dent->d_type isn't showing the type
    猜你喜欢
    • 2019-08-03
    • 1970-01-01
    • 2011-02-28
    • 2018-05-08
    • 2013-03-05
    • 2013-03-04
    • 2015-11-11
    • 2013-10-26
    • 1970-01-01
    相关资源
    最近更新 更多