Рекурсивно перечислить содержимое каталога, а также проверить, является ли файл каталог - PullRequest
2 голосов
/ 19 ноября 2011

Я пытаюсь изучить ассемблер, поэтому терпите меня, если мои проблемы элементарные

Следующий код сканирует каталог и распечатывает все файлы и каталоги в нем, кроме тех, которые начинаются с точки.Кажется, он работает нормально.

Однако, когда я раскомментирую строку 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', вызывая егоперечислить файлы там и файлы в 'тесте' дважды

[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 голосов
/ 19 ноября 2011

Ваш код выглядит почти нормально. Проблема связана с именем пути, возвращаемым readdir, которое вы хотите использовать для следующего рекурсивного шага. Возвращенное имя пути не относится к текущему рабочему каталогу.

Массивный вывод вы видите, когда каталог test/ содержит файл с именем test. Когда ваш цикл увидит это имя файла, вы передадите его в opendir, что означает, что вы просто снова откроете тот же каталог test/, вызывая бесконечную рекурсию, пока у вас не закончатся файловые дескрипторы.

Один из способов решить эту проблему - вызвать chdir после успешного opendir (а также chdir вернуться в родительский каталог после closedir), чтобы рабочий каталог всегда указывал на тот, который вы в настоящее время проверяю.


Кроме того, я хотел бы проверить, является ли файл каталогом или нет.

Структура dirent, возвращаемая readdir , имеет элемент d_type (со смещением 10), который вы можете проверить:

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