Мое почтение всем. Написал простенький обход дерева файловой системы (для себя) на asm под Gentoo. Столкнулся с необычной ситуацией. Код выглядит так: Код (Text): format ELF extrn printf extrn chdir extrn opendir extrn readdir extrn telldir extrn seekdir extrn closedir extrn exit extrn lstat extrn errno extrn perror extrn strcpy extrn strcmp public main struc stat_b st_dev, pad, st_ino, st_mode, st_nlink, st_uid, st_gid, st_rdev, pad2, st_size, st_blksize, st_blocks, st_atime, st_mtime, st_ctime, pad3 { .st_dev dq st_dev; /* ID of device containing file */ .pad dd pad; /* fucking padding */ .st_ino dd st_ino; /* inode number */ .st_mode dd st_mode; /* protection */ .st_nlink dd st_nlink; /* number of hard links */ .st_uid dd st_uid; /* user ID of owner */ .st_gid dd st_gid; /* group ID of owner */ .st_rdev dq st_rdev; /* device ID (if special file) */ .pad2 dd pad2; /* fucking padding2 */ .st_size dd st_size; /* total size, in bytes */ .st_blksize dd st_blksize; /* blocksize for filesystem I/O */ .st_blocks dd st_blocks; /* number of blocks allocated */ .st_atime dq st_atime; /* time of last access */ .st_mtime dq st_mtime; /* time of last modification */ .st_ctime dq st_ctime; /* time of last status change */ .pad3 dq pad3; /* fuckin fuckin padding3? */ } NAME_MAX = 0x100 S_IFDIR = 0x4000 ;directory S_IFMT = 0xF000 ;mask main: mov eax, [esp + 0x8] mov edx, [eax] mov [prog], edx mov eax, [eax + 0x4] push eax call parse_dir xor eax, eax ret err: push dword [esp + 0x4] call perror call exit warn: push dword [esp + 0x4] call perror add esp, 0x4 ret 0x4 parse_dir: sub esp, 0x4 push dword [esp + 0x8] call chdir add esp, 0x4 or eax, eax jz @f push chdr call warn jmp .locret @@: push dot call opendir add esp, 0x4 or eax, eax jnz @f push opndr call warn jmp .chdir @@: mov [esp], eax .l00p: push dword [esp] call readdir add esp, 0x4 or eax, eax jnz @f cmp dword [errno], 0x0 jz .closedir push rddr call warn jmp .closedir @@: lea ebx, [eax + 0xB] push ebx push dot call strcmp add esp, 0x8 or eax, eax jz .l00p push ebx push dotdot call strcmp add esp, 0x8 or eax, eax jz .l00p push stat_buff push ebx call lstat add esp, 0x8 or eax, eax jz @f push stt call warn jmp .closedir @@: mov eax, [stat_buff.st_mode] and eax, S_IFMT cmp eax, S_IFDIR jnz .nextf push ebx push name_buff call strcpy add esp, 0x8 push dword [esp] call telldir add esp, 0x4 mov ebx, eax push dword [esp] call closedir add esp, 0x4 mov [esp], ebx push name_buff call parse_dir push dot call opendir add esp, 0x4 mov ebx, eax push dword [esp] push ebx call seekdir add esp, 0x8 mov [esp], ebx .nextf: jmp .l00p .closedir: push dword [esp] call closedir add esp, 0x4 .chdir: push dotdot call chdir add esp, 0x4 .locret: add esp, 0x4 ret 0x4 prog: dd ? name_buff: db NAME_MAX dup 0x0 stat_buff stat_b 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 dot: db '.', 0x0 dotdot: db '..', 0x0 opndr: db 'opendir: ', 0x0 rddr: db 'readdir: ', 0x0 stt: db 'stat: ', 0x0 chdr: db 'chdir: ', 0x0 Но если натравить исполняемый, например, на такую директорию mkdir t && cd t && mkdir t && chmod 000 t То после выхода из вложения ф-ии, где chdir сделать не удалось, последующая операция readdir вернет 'Illegal seek' -- ошибку, котрой даже нет в man 3 readdir. Отчего такое может происходить? Заранее благодарен.
ну происходит то понятно отчего вызов seekdir() перед .nextf устанавливает неправильное смещение (а т. к seekdir() не устанавливает errno и ничего не возвращает, мы ошибку определить не можем) далее, естественно, вызов readdir() обламывается остается выяснить, почему значение смещения неправильное
n0name Странно, у меня ничего подобного в мане не сказано: Но в любом случае, когда я делаю почти то же самое -- т.е. просто открываю директорию, читаю 3 файла ("." и "..", "t"), сохраняю позицию, закрываю, открываю, восстанавливаю позицию и читаю -- readdir возвращает NULL, а в errno лежит success. Как и должно быть...
rei3er В том-то и дело -- я посмотрел в gdb (и printf'ами выводил) -- то, что получено из telldir передается в seekdir. Не может же telldir врать. Я уж думал, что тип off_t занимает 8 байт, но sizeof(off_t) показывает 4.
Кстати, если в директории (первой t) есть файлы, помимо вложенной директории t, к которой нет доступа, то они нормально возвращаются ф-ией readdir. Когда она доходит до конца директории, то завершается с Illegal seek.
Mika0x65 а если попробовать вместо вызова seekdir() вызывать в цикле readdir() + telldir() до момента, пока значение, возвращаемое telldir() не станет равно сохраненному Код (Text): ... @@: push ebx call telldir add esp, 4 cmp eax, dword [esp] jz @F push ebx call readdir add esp, 4 jmp @B @@: ... какой результат?
rei3er Сделал так. Результат тот же. У меня было подозрение -- не порчу ли где-нибудь структуру struct DIR. Но, вроде, нигде гнилых указателей нет.
Mika0x65 сделай так Код (Text): parse_dir: sub esp, 0x4 push dword [esp + 0x8] call chdir add esp, 0x4 or eax, eax jz @f push chdr call warn mov dword [errno], 0 ; обнуляем errno jmp .locret @@:
rei3er Спасибо, помогло! Только непонятно, errno же должна устанавливаться последующим (успешным) вызовом readdir?
Mika0x65 если функция завершается успешно, errno не обнуляется да и зачем ей обнуляться, если нет ошибки? кстати, какая у тебя версия glibc? по тому, как идет обращение к errno, можно предположить, что довольно старая
rei3er Точно. Невнимательно man 3 errno прочитал . Насчет вресии точно не знаю, т.к. машину собирал не я (удаленный сервер). ls -l /lib/libc* -rwxr-xr-x 1 root root 1216736 Mar 19 2006 /lib/libc-2.3.5.so lrwxrwxrwx 1 root root 13 Feb 8 2007 /lib/libc.so.6 -> libc-2.3.5.so