1
\$\begingroup\$

I needed to use some c++ code on both standard Linux and the Android variant, and the code will use filesystem library but it isn't yet in the ndk.
So I'm making a one to use on Android
It will be also usable on Linux because working with files and dirs go through the linux api which is the same on android

So, this is my implementation of the remove_all() function.
It reports errors in int variable and may be wrapped later with a function that will translate expected errno to proper c++ err_code.
It can't delete a dir that contains subdir with files exceeding the file descriptors limit for current process.
Also it can take the whole stack if called recursively too many times.
My filesystem implementation uses a thread local variable (int filesystem_errno) to store errors if needed.

size_t remove_all(const filesystem::path& p, int& err = filesystem_errno)
{

    int dir_fd = open(p.c_str(),O_RDONLY|O_DIRECTORY);
    if (dir_fd < 0)
    {
        err = errno;
        return 0;
    }
    if (p.native().length() > PATH_MAX)
    {
        close(dir_fd);
        err = ENAMETOOLONG;
        return 0;
    }
    const int BUFF_SIZE = 1024;
    char buffer[BUFF_SIZE];
    char subdir[PATH_MAX];
    int base_dir_len = strlen(p.c_str());
    memcpy(subdir,p.c_str(),base_dir_len);
    if (subdir[base_dir_len - 1] != '/')
    {
        subdir[base_dir_len] = '/';
        ++base_dir_len;
    }
    char *subdir_ptr = subdir;
    subdir_ptr += base_dir_len;
    struct linux_dirent
    {
        unsigned long inod;
        off64_t off;
        unsigned short len;
        unsigned char type;
        char name[];
    };
    struct linux_dirent32
    {
        unsigned long inod;
        off_t off;
        unsigned short len;
        char name[];
    };
    linux_dirent *ldir_ptr;
    linux_dirent32 *ldir_ptr32;
    size_t fd_count = 0;
    while(true)
    {
        int nread = syscall(SYS_getdents64,dir_fd,buffer,BUFF_SIZE);
        if (!nread)
            break;
        if (nread == -1)
        {
            err = errno;
            close(dir_fd);
            return fd_count;    
        }
        for (int pos = 0; pos < nread; pos += ldir_ptr->len)
        {
            ldir_ptr = (linux_dirent*)(buffer + pos);
            // skip . and ..
            if (ldir_ptr->name[0] == '.')
            {
                if (ldir_ptr->name[1] == 0)
                    continue;
                else if (ldir_ptr->name[1] == '.')
                    if (ldir_ptr->name[2] == 0)
                        continue;
            }

            int subdir_len = strlen(ldir_ptr->name);
            if ((subdir_len + base_dir_len) > PATH_MAX)
            {
                close(dir_fd);
                err = ENAMETOOLONG;
                return fd_count;
            }   memcpy(subdir_ptr,ldir_ptr->name,subdir_len);
            subdir_ptr[subdir_len] = 0;

            if (ldir_ptr->type == DT_DIR)
            {
                size_t deleted_dirs = 0;
                if (!rmdir(subdir)) // empty dir
                    ++deleted_dirs; 
                else if (!(deleted_dirs = filesystem::remove_all(subdir,err)))
                {
                    close(dir_fd);
                    return fd_count;
                }
                fd_count += deleted_dirs;
            }
            else if (ldir_ptr->type != DT_UNKNOWN)
            {
                if(unlink(subdir))
                {
                    err = errno;
                    close(dir_fd);
                    return fd_count;
                }
                ++fd_count;
            }
            else
            {
                struct stat st;
                if (stat(subdir,&st))
                {
                    err = errno;
                    close(dir_fd);
                    return fd_count;
                }
                if (filesystem::is_directory(st))
                {
                    size_t deleted_dirs = 0;
                    if (!rmdir(subdir))
                        ++deleted_dirs;
                    else if (!(deleted_dirs = filesystem::remove_all(subdir,err)))
                    {
                        close(dir_fd);
                        return fd_count;
                    }
                    fd_count += deleted_dirs;
                }
                else 
                {
                    if (unlink(subdir))
                    {
                        err = errno;
                        close(dir_fd);
                        return fd_count;
                    }
                    ++fd_count;
                }
            }
        }
    }

    close(dir_fd);
    if (rmdir(p.c_str()))
    {
        err = errno;
        return fd_count;
    }
    err = 0;
    ++fd_count;
    return fd_count; 
}
\$\endgroup\$
  • \$\begingroup\$ Is there a reason to not use opendir/readdir API? \$\endgroup\$ – vnp Apr 7 at 19:26
  • \$\begingroup\$ This is much faster for dirs with too many files . \$\endgroup\$ – prog511 Apr 7 at 19:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.