|
@@ -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 */
|