ls chamadas readdir() (ou getdents() ) no próprio diretório, o que equivale a "ler" o diretório. Acho que alguns Unixes implementam isso como chamadas reais para read() , como se o diretório fosse um arquivo. Isso funciona, desde que você tenha permissão de leitura para o diretório, mas só dá os nomes dos arquivos.
Além disso, ls geralmente deseja encontrar os tipos de arquivos (para mostrar diferentes tipos com cores diferentes ou para colocar barras e asteriscos etc. com ls -F ). Mas encontrar o tipo geralmente requer chamar stat() nos próprios arquivos, e isso requer permissão de acesso ( +x ) para o diretório, da mesma maneira que a abertura dos arquivos requereria. E você não tem isso.