Skip to content

Commit b348da6

Browse files
committed
add getdents64 syscall
1 parent 1132711 commit b348da6

File tree

2 files changed

+49
-67
lines changed

2 files changed

+49
-67
lines changed

darwin-user/irix/target_syscall.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,7 @@ struct target_dirent64 {
5454
abi_ushort d_reclen;
5555
char d_name[1];
5656
};
57+
/* size of struct target_dirent without the name array */
58+
#define TARGET_DIRENT64_LEN (offsetof(struct target_dirent64, d_name))
5759

5860
#endif

darwin-user/syscall.c

Lines changed: 47 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -10893,7 +10893,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
1089310893
} else {
1089410894
ret = bytes_used;
1089510895
}
10896-
unlock_user(target_dirp, arg2, ret);
10896+
unlock_user(target_dirp, arg2, count);
1089710897
// Don't close the `DIR *` pointer, as that will close the fd
1089810898
// which was used in `fdopendir`
1089910899
// closedir(dirp);
@@ -10910,83 +10910,63 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
1091010910
#ifdef TARGET_NR_ngetdents64
1091110911
case TARGET_NR_ngetdents64:
1091210912
#endif
10913-
#if defined(TARGET_NR_getdents64) && defined(__NR_getdents64)
10913+
#ifdef TARGET_NR_getdents64
1091410914
case TARGET_NR_getdents64:
10915-
#if defined TARGET_ABI_IRIX || defined TARGET_ABI_SOLARIS
10916-
{
10915+
{
1091710916
struct target_dirent64 *target_dirp;
10918-
struct linux_dirent64 *dirp;
10917+
DIR *dirp;
10918+
abi_long dir_fd = arg1;
1091910919
abi_long count = arg3;
1092010920

10921-
dirp = malloc(count);
10922-
if (!dirp) {
10923-
ret = -TARGET_ENOMEM;
10921+
dirp = fdopendir(dir_fd);
10922+
if (!dirp) {
10923+
ret = -host_to_target_errno(errno);
1092410924
goto fail;
1092510925
}
1092610926

10927-
ret = get_errno(sys_getdents64(arg1, dirp, count));
10928-
if (!is_error(ret)) {
10929-
struct linux_dirent64 *de;
10930-
struct target_dirent64 *tde;
10931-
int len = ret;
10932-
int reclen, treclen;
10933-
int count1, tnamelen;
10934-
10935-
count1 = 0;
10936-
de = dirp;
10937-
if (!(target_dirp = lock_user(VERIFY_WRITE, arg2, count, 0)))
10938-
goto efault;
10939-
tde = target_dirp;
10940-
while (len > 0) {
10941-
reclen = de->d_reclen;
10942-
tnamelen = reclen - offsetof(struct linux_dirent64, d_name);
10943-
tnamelen = strnlen(de->d_name, tnamelen) + 1;
10944-
assert(tnamelen > 0);
10945-
treclen = tnamelen + offsetof(struct target_dirent64, d_name);
10946-
treclen = QEMU_ALIGN_UP(treclen, sizeof(abi_llong));
10947-
/* XXX: avoid buffer overflow, for the price of lost entries */
10948-
if (count1 + treclen > count)
10949-
break;
10950-
__put_user(treclen, &tde->d_reclen);
10951-
__put_user(de->d_ino, &tde->d_ino);
10952-
__put_user(de->d_off, &tde->d_off);
10953-
memcpy(tde->d_name, de->d_name, tnamelen);
10954-
de = (struct linux_dirent64 *)((char *)de + reclen);
10955-
len -= reclen;
10956-
tde = (struct target_dirent64 *)((char *)tde + treclen);
10957-
count1 += treclen;
10958-
}
10959-
ret = count1;
10960-
unlock_user(target_dirp, arg2, ret);
10961-
}
10962-
free(dirp);
10963-
}
10964-
#else
10965-
{
10966-
struct linux_dirent64 *dirp;
10967-
abi_long count = arg3;
10968-
if (!(dirp = lock_user(VERIFY_WRITE, arg2, count, 0)))
10927+
// collect readdir calls into getdents dirp * buffer
10928+
target_dirp = lock_user(VERIFY_WRITE, arg2, count, 0);
10929+
if (!target_dirp) {
1096910930
goto efault;
10970-
ret = get_errno(sys_getdents64(arg1, dirp, count));
10971-
if (!is_error(ret)) {
10972-
struct linux_dirent64 *de;
10973-
int len = ret;
10974-
int reclen;
10975-
de = dirp;
10976-
while (len > 0) {
10977-
reclen = de->d_reclen;
10978-
if (reclen > len)
10979-
break;
10980-
__put_user(reclen, &de->d_reclen);
10981-
tswap64s((uint64_t *)&de->d_ino);
10982-
tswap64s((uint64_t *)&de->d_off);
10983-
de = (struct linux_dirent64 *)((char *)de + reclen);
10984-
len -= reclen;
10931+
}
10932+
10933+
struct dirent *de;
10934+
struct target_dirent64 *tde = target_dirp;
10935+
uint bytes_used = 0;
10936+
uint target_reclen;
10937+
uint name_len;
10938+
10939+
errno = 0;
10940+
while ((de = readdir(dirp))) {
10941+
// check if new entry will overflow the target buffer
10942+
name_len = de->d_namlen + 1;
10943+
target_reclen = TARGET_DIRENT64_LEN + name_len;
10944+
// is this the correct way to align this structure..?
10945+
target_reclen = QEMU_ALIGN_UP(target_reclen, __alignof(struct target_dirent64));
10946+
if (bytes_used + target_reclen > count) {
10947+
break;
1098510948
}
10949+
// translate from host to target dirent
10950+
__put_user(de->d_ino, &tde->d_ino);
10951+
__put_user(de->d_seekoff, &tde->d_off);
10952+
__put_user(target_reclen, &tde->d_reclen);
10953+
memcpy(tde->d_name, de->d_name, name_len);
10954+
// advance pointer in target space
10955+
// host pointer is moved by calling readdir
10956+
tde = (struct target_dirent64 *)((char *)tde + target_reclen);
10957+
bytes_used += target_reclen;
1098610958
}
10987-
unlock_user(dirp, arg2, ret);
10959+
10960+
if (errno != 0) {
10961+
ret = -host_to_target_errno(errno);
10962+
} else {
10963+
ret = bytes_used;
10964+
}
10965+
unlock_user(target_dirp, arg2, count);
10966+
// Don't close the `DIR *` pointer, as that will close the fd
10967+
// which was used in `fdopendir`
10968+
// closedir(dirp);
1098810969
}
10989-
#endif
1099010970
#ifdef TARGET_NR_ngetdents64
1099110971
if (ret >= 0 && num == TARGET_NR_ngetdents64) {
1099210972
abi_long *p = lock_user(VERIFY_WRITE, arg4, sizeof(abi_long), 0);

0 commit comments

Comments
 (0)