/*************************************************************** SYS-GETDENTS.C Linux/i386/ILP32 implementation of __cpu_sys_getdents(). PART OF : CPU - library . USAGE : Internal only . Copyright (C) 2026 by Andrew V.Kosteltsev. All Rights Reserved. ***************************************************************/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #define __CPU_HOST_DIRENT_BUFFER 4096U struct __cpu_linux_dirent64 { uint64_t d_ino; int64_t d_off; unsigned short d_reclen; unsigned char d_type; char d_name[]; }; static int __cpu_dirent_utf8_valid( const unsigned char *s, size_t n ) { size_t i = 0; while( i < n ) { unsigned int c = s[i++]; if( c < 0x80U ) continue; if( c >= 0xc2U && c <= 0xdfU ) { if( i >= n || ( s[i] & 0xc0U ) != 0x80U ) return( 0 ); ++i; continue; } if( c == 0xe0U ) { if( i + 1 >= n || s[i] < 0xa0U || s[i] > 0xbfU || ( s[i + 1] & 0xc0U ) != 0x80U ) return( 0 ); i += 2; continue; } if( c >= 0xe1U && c <= 0xecU ) { if( i + 1 >= n || ( s[i] & 0xc0U ) != 0x80U || ( s[i + 1] & 0xc0U ) != 0x80U ) return( 0 ); i += 2; continue; } if( c == 0xedU ) { if( i + 1 >= n || s[i] < 0x80U || s[i] > 0x9fU || ( s[i + 1] & 0xc0U ) != 0x80U ) return( 0 ); i += 2; continue; } if( c >= 0xeeU && c <= 0xefU ) { if( i + 1 >= n || ( s[i] & 0xc0U ) != 0x80U || ( s[i + 1] & 0xc0U ) != 0x80U ) return( 0 ); i += 2; continue; } if( c == 0xf0U ) { if( i + 2 >= n || s[i] < 0x90U || s[i] > 0xbfU || ( s[i + 1] & 0xc0U ) != 0x80U || ( s[i + 2] & 0xc0U ) != 0x80U ) return( 0 ); i += 3; continue; } if( c >= 0xf1U && c <= 0xf3U ) { if( i + 2 >= n || ( s[i] & 0xc0U ) != 0x80U || ( s[i + 1] & 0xc0U ) != 0x80U || ( s[i + 2] & 0xc0U ) != 0x80U ) return( 0 ); i += 3; continue; } if( c == 0xf4U ) { if( i + 2 >= n || s[i] < 0x80U || s[i] > 0x8fU || ( s[i + 1] & 0xc0U ) != 0x80U || ( s[i + 2] & 0xc0U ) != 0x80U ) return( 0 ); i += 3; continue; } return( 0 ); } return( 1 ); } static __mpu_uint64_t __cpu_dirent_type( unsigned char type ) { switch( type ) { #ifdef DT_FIFO case DT_FIFO: return( __CPU_DT_FIFO ); #endif #ifdef DT_CHR case DT_CHR: return( __CPU_DT_CHR ); #endif #ifdef DT_DIR case DT_DIR: return( __CPU_DT_DIR ); #endif #ifdef DT_BLK case DT_BLK: return( __CPU_DT_BLK ); #endif #ifdef DT_REG case DT_REG: return( __CPU_DT_REG ); #endif #ifdef DT_LNK case DT_LNK: return( __CPU_DT_LNK ); #endif #ifdef DT_SOCK case DT_SOCK: return( __CPU_DT_SOCK ); #endif default: return( __CPU_DT_UNKNOWN ); } } __mpu_int64_t __cpu_sys_getdents( int fd, struct __cpu_dirent *dirp, __mpu_uint64_t count ) { unsigned char hostbuf[__CPU_HOST_DIRENT_BUFFER]; struct stat st; off_t start; off_t resume; ssize_t nread; size_t pos = 0; size_t nrecords = 0; size_t maxrecords; if( !dirp ) return( -(__mpu_int64_t)__MCPU_EFAULT ); if( count < __CPU_DIRENT_SIZE ) return( -(__mpu_int64_t)__MCPU_EINVAL ); maxrecords = (size_t)( count / __CPU_DIRENT_SIZE ); if( fstat( fd, &st ) < 0 ) return( -(__mpu_int64_t)__cpu_errno_from_host( errno ) ); if( !S_ISDIR( st.st_mode ) ) return( -(__mpu_int64_t)__MCPU_ENOTDIR ); start = lseek( fd, (off_t)0, SEEK_CUR ); if( start == (off_t)-1 ) return( -(__mpu_int64_t)__cpu_errno_from_host( errno ) ); resume = start; nread = syscall( SYS_getdents64, fd, hostbuf, sizeof( hostbuf ) ); if( nread < 0 ) return( -(__mpu_int64_t)__cpu_errno_from_host( errno ) ); if( nread == 0 ) return( 0 ); while( pos < (size_t)nread ) { struct __cpu_linux_dirent64 *de; const char *nul; size_t namespace; size_t namelen; if( (size_t)nread - pos < offsetof( struct __cpu_linux_dirent64, d_name ) + 1U ) goto malformed; de = (struct __cpu_linux_dirent64 *)( hostbuf + pos ); if( de->d_reclen < offsetof( struct __cpu_linux_dirent64, d_name ) + 1U || de->d_reclen > (size_t)nread - pos ) goto malformed; if( nrecords >= maxrecords ) break; namespace = de->d_reclen - offsetof( struct __cpu_linux_dirent64, d_name ); nul = (const char *)memchr( de->d_name, '\0', namespace ); if( !nul ) goto malformed; namelen = (size_t)( nul - de->d_name ); if( namelen >= __CPU_DIRENT_NAME_SIZE ) { if( lseek( fd, resume, SEEK_SET ) == (off_t)-1 ) return( -(__mpu_int64_t)__cpu_errno_from_host( errno ) ); return( nrecords ? (__mpu_int64_t)( nrecords * __CPU_DIRENT_SIZE ) : -(__mpu_int64_t)__MCPU_ENAMETOOLONG ); } if( !__cpu_dirent_utf8_valid( (const unsigned char *)de->d_name, namelen ) ) { if( lseek( fd, resume, SEEK_SET ) == (off_t)-1 ) return( -(__mpu_int64_t)__cpu_errno_from_host( errno ) ); return( nrecords ? (__mpu_int64_t)( nrecords * __CPU_DIRENT_SIZE ) : -(__mpu_int64_t)__MCPU_EILSEQ ); } dirp[nrecords].d_ino = (__mpu_uint64_t)de->d_ino; dirp[nrecords].d_off = (__mpu_int64_t)de->d_off; dirp[nrecords].d_reclen = __CPU_DIRENT_SIZE; dirp[nrecords].d_type = __cpu_dirent_type( de->d_type ); memset( dirp[nrecords].d_name, 0, __CPU_DIRENT_NAME_SIZE ); memcpy( dirp[nrecords].d_name, de->d_name, namelen ); resume = (off_t)de->d_off; ++nrecords; pos += de->d_reclen; } if( pos < (size_t)nread ) { if( lseek( fd, resume, SEEK_SET ) == (off_t)-1 ) return( -(__mpu_int64_t)__cpu_errno_from_host( errno ) ); } return( (__mpu_int64_t)( nrecords * __CPU_DIRENT_SIZE ) ); malformed: if( lseek( fd, start, SEEK_SET ) == (off_t)-1 ) return( -(__mpu_int64_t)__cpu_errno_from_host( errno ) ); return( -(__mpu_int64_t)__MCPU_EIO ); } /* End of __cpu_sys_getdents() */