Search code examples
linuxassemblyx86unlink

Delete all files including itself in folder in Linux assembly x86


I write this code and it would not unlink a file if invoked from the same directory as the file to delete, it would unlink files from other dirs

.section .data
fpath:
    .asciz "/home/user/filename"  # path to file to delete

.section .text
.globl _start
_start:
movl $10, %eax       # unlink syscall 
movl $fpath, %ebx    # path to file to delete
int  $0x80 

movl %eax, %ebx      # put syscall ret value in ebx
movl $1, %eax        # exit syscall
int  $0x80

What i want is to unlink all files (including itself) in the directory in which it is running.


Solution

  • You need to recursively delete the directory contents before you can delete the directory itself. The following is in C but uses no standard library functions, only syscalls:

    #define _LARGEFILE64_SOURCE
    #define _FILE_OFFSET_BITS 64
    #include <dirent.h>
    #include <fcntl.h>
    #include <sys/syscall.h>
    #include <sys/types.h>
    #include <unistd.h>
    struct linux_dirent64 {
        ino64_t d_ino;
        off64_t d_off;
        unsigned short d_reclen;
        unsigned char d_type;
        char d_name[];
    };
    static void rmrf(int dfd, const char *name) {
        unsigned char buffer[16384];
        struct linux_dirent64 *dirent;
        int fd, off, size;
        fd = syscall(SYS_openat, dfd, name, O_RDONLY | O_DIRECTORY);
        if (fd == -1) {
            syscall(SYS_exit, 1);
        }
        do {
            size = syscall(SYS_getdents64, fd, buffer, sizeof(buffer));
            if (size < 0) {
                syscall(SYS_exit, 1);
            }
            for (off = 0; off < size; off += dirent->d_reclen) {
                dirent = (struct linux_dirent64 *)&buffer[off];
                if (dirent->d_name[0] == '.' &&
                    (dirent->d_name[1] == '\0' ||
                     (dirent->d_name[1] == '.' &&
                      (dirent->d_name[2] == '\0')))) {
                    continue;
                }
                if (dirent->d_type != DT_DIR) {
                    if (!syscall(SYS_unlinkat, fd, dirent->d_name, 0)) {
                        continue;
                    }
                    if (dirent->d_type != DT_UNKNOWN) {
                        syscall(SYS_exit, 1);
                    }
                }
                rmrf(fd, dirent->d_name);
            }
        } while (off);
        syscall(SYS_close, fd);
        if (syscall(SYS_unlinkat, dfd, name, AT_REMOVEDIR)) {
            syscall(SYS_exit, -1);
        }
    }
    int main() {
        const char fpath[] = "/home/user/filename";
        if (syscall(SYS_unlink, fpath)) {
            rmrf(0, fpath);
        }
        syscall(SYS_exit, 0);
    }