|  | @@ -26,6 +26,7 @@
 | 
	
		
			
				|  |  |  #include "pal.h"
 | 
	
		
			
				|  |  |  #include "pal_internal.h"
 | 
	
		
			
				|  |  |  #include "pal_linux.h"
 | 
	
		
			
				|  |  | +#include "pal_linux_error.h"
 | 
	
		
			
				|  |  |  #include "pal_debug.h"
 | 
	
		
			
				|  |  |  #include "pal_error.h"
 | 
	
		
			
				|  |  |  #include "api.h"
 | 
	
	
		
			
				|  | @@ -406,80 +407,85 @@ struct linux_dirent64 {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #define DIRBUF_SIZE     1024
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +static inline bool is_dot_or_dotdot(const char* name) {
 | 
	
		
			
				|  |  | +    return (name[0] == '.' && !name[1]) || (name[0] == '.' && name[1] == '.' && !name[2]);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  /* 'read' operation for directory stream. Directory stream will not
 | 
	
		
			
				|  |  |     need a 'write' operation. */
 | 
	
		
			
				|  |  | -int64_t dir_read (PAL_HANDLE handle, uint64_t offset, size_t count, void * buf)
 | 
	
		
			
				|  |  | -{
 | 
	
		
			
				|  |  | -    if (offset)
 | 
	
		
			
				|  |  | -        return -PAL_ERROR_INVAL;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    void * dent_buf = (void *) handle->dir.buf ? : __alloca(DIRBUF_SIZE);
 | 
	
		
			
				|  |  | -    void * ptr = (void *) handle->dir.ptr;
 | 
	
		
			
				|  |  | -    void * end = (void *) handle->dir.end;
 | 
	
		
			
				|  |  | -    int bytes = 0;
 | 
	
		
			
				|  |  | +static int64_t dir_read(PAL_HANDLE handle, uint64_t offset, size_t count, void* _buf) {
 | 
	
		
			
				|  |  | +    size_t bytes_written = 0;
 | 
	
		
			
				|  |  | +    char* buf = (char*)_buf;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (ptr && ptr < end)
 | 
	
		
			
				|  |  | -        goto output;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    do {
 | 
	
		
			
				|  |  | -        if (handle->dir.endofstream)
 | 
	
		
			
				|  |  | -            break;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        int size = INLINE_SYSCALL(getdents64, 3, handle->dir.fd, dent_buf,
 | 
	
		
			
				|  |  | -                                  DIRBUF_SIZE);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        if (IS_ERR(size))
 | 
	
		
			
				|  |  | -            return -PAL_ERROR_DENIED;
 | 
	
		
			
				|  |  | +    if (offset) {
 | 
	
		
			
				|  |  | +        return -PAL_ERROR_INVAL;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        if (size == 0) {
 | 
	
		
			
				|  |  | -            handle->dir.endofstream = PAL_TRUE;
 | 
	
		
			
				|  |  | -            break;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | +    if (handle->dir.endofstream == PAL_TRUE) {
 | 
	
		
			
				|  |  | +        return -PAL_ERROR_ENDOFSTREAM;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        ptr = dent_buf;
 | 
	
		
			
				|  |  | -        end = dent_buf + size;
 | 
	
		
			
				|  |  | +    while (1) {
 | 
	
		
			
				|  |  | +        while ((char*)handle->dir.ptr < (char*)handle->dir.end) {
 | 
	
		
			
				|  |  | +            struct linux_dirent64* dirent = (struct linux_dirent64*)handle->dir.ptr;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -output:
 | 
	
		
			
				|  |  | -        while (ptr < end) {
 | 
	
		
			
				|  |  | -            struct linux_dirent64 * d = (struct linux_dirent64 *) ptr;
 | 
	
		
			
				|  |  | +            if (is_dot_or_dotdot(dirent->d_name)) {
 | 
	
		
			
				|  |  | +                goto skip;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            if (d->d_name[0] == '.' &&
 | 
	
		
			
				|  |  | -                (!d->d_name[1] || d->d_name[1] == '.'))
 | 
	
		
			
				|  |  | -                goto next;
 | 
	
		
			
				|  |  | +            bool is_dir = dirent->d_type == DT_DIR;
 | 
	
		
			
				|  |  | +            size_t len = strlen(dirent->d_name);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            bool isdir = (d->d_type == DT_DIR);
 | 
	
		
			
				|  |  | -            size_t len = strlen(d->d_name);
 | 
	
		
			
				|  |  | -            if (len + (isdir ? 2 : 1) > count)
 | 
	
		
			
				|  |  | -                break;
 | 
	
		
			
				|  |  | +            if (len + 1 + (is_dir ? 1 : 0) > count) {
 | 
	
		
			
				|  |  | +                goto out;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            memcpy(buf, d->d_name, len);
 | 
	
		
			
				|  |  | -            if (isdir)
 | 
	
		
			
				|  |  | -                ((char *) buf)[len++] = '/';
 | 
	
		
			
				|  |  | -            ((char *) buf)[len++] = '\0';
 | 
	
		
			
				|  |  | +            memcpy(buf, dirent->d_name, len);
 | 
	
		
			
				|  |  | +            if (is_dir) {
 | 
	
		
			
				|  |  | +                buf[len++] = '/';
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            buf[len++] = '\0';
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            bytes += len;
 | 
	
		
			
				|  |  |              buf += len;
 | 
	
		
			
				|  |  | +            bytes_written += len;
 | 
	
		
			
				|  |  |              count -= len;
 | 
	
		
			
				|  |  | -next:
 | 
	
		
			
				|  |  | -            ptr += d->d_reclen;
 | 
	
		
			
				|  |  | +skip:
 | 
	
		
			
				|  |  | +            handle->dir.ptr = (char*)handle->dir.ptr + dirent->d_reclen;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if (!count) {
 | 
	
		
			
				|  |  | +            /* No space left, returning */
 | 
	
		
			
				|  |  | +            goto out;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | -    } while (ptr == end);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (ptr < end) {
 | 
	
		
			
				|  |  | -        if (!handle->dir.buf)
 | 
	
		
			
				|  |  | -            handle->dir.buf = (PAL_PTR) malloc(DIRBUF_SIZE);
 | 
	
		
			
				|  |  | +        if (!handle->dir.buf) {
 | 
	
		
			
				|  |  | +            handle->dir.buf = (PAL_PTR)malloc(DIRBUF_SIZE);
 | 
	
		
			
				|  |  | +            if (!handle->dir.buf) {
 | 
	
		
			
				|  |  | +                return -PAL_ERROR_NOMEM;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        if ((void *) handle->dir.buf != ptr) {
 | 
	
		
			
				|  |  | -            memmove((void *) handle->dir.buf, ptr, end - ptr);
 | 
	
		
			
				|  |  | -            end = (void *) handle->dir.buf + (end - ptr);
 | 
	
		
			
				|  |  | -            ptr = (void *) handle->dir.buf;
 | 
	
		
			
				|  |  | +        int size = INLINE_SYSCALL(getdents64, 3, handle->dir.fd, handle->dir.buf, DIRBUF_SIZE);
 | 
	
		
			
				|  |  | +        if (IS_ERR(size)) {
 | 
	
		
			
				|  |  | +            /* If something was written just return that and pretend
 | 
	
		
			
				|  |  | +             * no error was seen - it will be caught next time. */
 | 
	
		
			
				|  |  | +            if (bytes_written) {
 | 
	
		
			
				|  |  | +                return bytes_written;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            return unix_to_pal_error(ERRNO(size));
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if (!size) {
 | 
	
		
			
				|  |  | +            handle->dir.endofstream = PAL_TRUE;
 | 
	
		
			
				|  |  | +            goto out;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        if (!bytes)
 | 
	
		
			
				|  |  | -            return -PAL_ERROR_OVERFLOW;
 | 
	
		
			
				|  |  | +        handle->dir.ptr = handle->dir.buf;
 | 
	
		
			
				|  |  | +        handle->dir.end = (char*)handle->dir.buf + size;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    return bytes ? : -PAL_ERROR_ENDOFSTREAM;
 | 
	
		
			
				|  |  | +out:
 | 
	
		
			
				|  |  | +    return (int64_t)bytes_written ? : -PAL_ERROR_ENDOFSTREAM;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /* 'close' operation of directory streams */
 |