linux-user: Split out do_getdents, do_getdents64
Retain all 3 implementations of getdents for now. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> Message-Id: <20211114103539.298686-2-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier <laurent@vivier.eu>
This commit is contained in:
parent
8627edfb3f
commit
fd08ddb9cb
@ -8137,6 +8137,176 @@ static int host_to_target_cpu_mask(const unsigned long *host_mask,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef TARGET_NR_getdents
|
||||
static int do_getdents(abi_long arg1, abi_long arg2, abi_long arg3)
|
||||
{
|
||||
int ret;
|
||||
|
||||
#ifdef EMULATE_GETDENTS_WITH_GETDENTS
|
||||
# if TARGET_ABI_BITS == 32 && HOST_LONG_BITS == 64
|
||||
struct target_dirent *target_dirp;
|
||||
struct linux_dirent *dirp;
|
||||
abi_long count = arg3;
|
||||
|
||||
dirp = g_try_malloc(count);
|
||||
if (!dirp) {
|
||||
return -TARGET_ENOMEM;
|
||||
}
|
||||
|
||||
ret = get_errno(sys_getdents(arg1, dirp, count));
|
||||
if (!is_error(ret)) {
|
||||
struct linux_dirent *de;
|
||||
struct target_dirent *tde;
|
||||
int len = ret;
|
||||
int reclen, treclen;
|
||||
int count1, tnamelen;
|
||||
|
||||
count1 = 0;
|
||||
de = dirp;
|
||||
target_dirp = lock_user(VERIFY_WRITE, arg2, count, 0);
|
||||
if (!target_dirp) {
|
||||
return -TARGET_EFAULT;
|
||||
}
|
||||
tde = target_dirp;
|
||||
while (len > 0) {
|
||||
reclen = de->d_reclen;
|
||||
tnamelen = reclen - offsetof(struct linux_dirent, d_name);
|
||||
assert(tnamelen >= 0);
|
||||
treclen = tnamelen + offsetof(struct target_dirent, d_name);
|
||||
assert(count1 + treclen <= count);
|
||||
tde->d_reclen = tswap16(treclen);
|
||||
tde->d_ino = tswapal(de->d_ino);
|
||||
tde->d_off = tswapal(de->d_off);
|
||||
memcpy(tde->d_name, de->d_name, tnamelen);
|
||||
de = (struct linux_dirent *)((char *)de + reclen);
|
||||
len -= reclen;
|
||||
tde = (struct target_dirent *)((char *)tde + treclen);
|
||||
count1 += treclen;
|
||||
}
|
||||
ret = count1;
|
||||
unlock_user(target_dirp, arg2, ret);
|
||||
}
|
||||
g_free(dirp);
|
||||
# else
|
||||
struct linux_dirent *dirp;
|
||||
abi_long count = arg3;
|
||||
|
||||
dirp = lock_user(VERIFY_WRITE, arg2, count, 0);
|
||||
if (!dirp) {
|
||||
return -TARGET_EFAULT;
|
||||
}
|
||||
ret = get_errno(sys_getdents(arg1, dirp, count));
|
||||
if (!is_error(ret)) {
|
||||
struct linux_dirent *de;
|
||||
int len = ret;
|
||||
int reclen;
|
||||
de = dirp;
|
||||
while (len > 0) {
|
||||
reclen = de->d_reclen;
|
||||
if (reclen > len) {
|
||||
break;
|
||||
}
|
||||
de->d_reclen = tswap16(reclen);
|
||||
tswapls(&de->d_ino);
|
||||
tswapls(&de->d_off);
|
||||
de = (struct linux_dirent *)((char *)de + reclen);
|
||||
len -= reclen;
|
||||
}
|
||||
}
|
||||
unlock_user(dirp, arg2, ret);
|
||||
# endif
|
||||
#else
|
||||
/* Implement getdents in terms of getdents64 */
|
||||
struct linux_dirent64 *dirp;
|
||||
abi_long count = arg3;
|
||||
|
||||
dirp = lock_user(VERIFY_WRITE, arg2, count, 0);
|
||||
if (!dirp) {
|
||||
return -TARGET_EFAULT;
|
||||
}
|
||||
ret = get_errno(sys_getdents64(arg1, dirp, count));
|
||||
if (!is_error(ret)) {
|
||||
/*
|
||||
* Convert the dirent64 structs to target dirent. We do this
|
||||
* in-place, since we can guarantee that a target_dirent is no
|
||||
* larger than a dirent64; however this means we have to be
|
||||
* careful to read everything before writing in the new format.
|
||||
*/
|
||||
struct linux_dirent64 *de;
|
||||
struct target_dirent *tde;
|
||||
int len = ret;
|
||||
int tlen = 0;
|
||||
|
||||
de = dirp;
|
||||
tde = (struct target_dirent *)dirp;
|
||||
while (len > 0) {
|
||||
int namelen, treclen;
|
||||
int reclen = de->d_reclen;
|
||||
uint64_t ino = de->d_ino;
|
||||
int64_t off = de->d_off;
|
||||
uint8_t type = de->d_type;
|
||||
|
||||
namelen = strlen(de->d_name);
|
||||
treclen = offsetof(struct target_dirent, d_name) + namelen + 2;
|
||||
treclen = QEMU_ALIGN_UP(treclen, sizeof(abi_long));
|
||||
|
||||
memmove(tde->d_name, de->d_name, namelen + 1);
|
||||
tde->d_ino = tswapal(ino);
|
||||
tde->d_off = tswapal(off);
|
||||
tde->d_reclen = tswap16(treclen);
|
||||
/*
|
||||
* The target_dirent type is in what was formerly a padding
|
||||
* byte at the end of the structure:
|
||||
*/
|
||||
*(((char *)tde) + treclen - 1) = type;
|
||||
|
||||
de = (struct linux_dirent64 *)((char *)de + reclen);
|
||||
tde = (struct target_dirent *)((char *)tde + treclen);
|
||||
len -= reclen;
|
||||
tlen += treclen;
|
||||
}
|
||||
ret = tlen;
|
||||
}
|
||||
unlock_user(dirp, arg2, ret);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
#endif /* TARGET_NR_getdents */
|
||||
|
||||
#if defined(TARGET_NR_getdents64) && defined(__NR_getdents64)
|
||||
static int do_getdents64(abi_long arg1, abi_long arg2, abi_long arg3)
|
||||
{
|
||||
struct linux_dirent64 *dirp;
|
||||
abi_long count = arg3;
|
||||
int ret;
|
||||
|
||||
dirp = lock_user(VERIFY_WRITE, arg2, count, 0);
|
||||
if (!dirp) {
|
||||
return -TARGET_EFAULT;
|
||||
}
|
||||
ret = get_errno(sys_getdents64(arg1, dirp, count));
|
||||
if (!is_error(ret)) {
|
||||
struct linux_dirent64 *de;
|
||||
int len = ret;
|
||||
int reclen;
|
||||
de = dirp;
|
||||
while (len > 0) {
|
||||
reclen = de->d_reclen;
|
||||
if (reclen > len) {
|
||||
break;
|
||||
}
|
||||
de->d_reclen = tswap16(reclen);
|
||||
tswap64s((uint64_t *)&de->d_ino);
|
||||
tswap64s((uint64_t *)&de->d_off);
|
||||
de = (struct linux_dirent64 *)((char *)de + reclen);
|
||||
len -= reclen;
|
||||
}
|
||||
}
|
||||
unlock_user(dirp, arg2, ret);
|
||||
return ret;
|
||||
}
|
||||
#endif /* TARGET_NR_getdents64 */
|
||||
|
||||
#if defined(TARGET_NR_pivot_root) && defined(__NR_pivot_root)
|
||||
_syscall2(int, pivot_root, const char *, new_root, const char *, put_old)
|
||||
#endif
|
||||
@ -10227,162 +10397,11 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
|
||||
#endif
|
||||
#ifdef TARGET_NR_getdents
|
||||
case TARGET_NR_getdents:
|
||||
#ifdef EMULATE_GETDENTS_WITH_GETDENTS
|
||||
#if TARGET_ABI_BITS == 32 && HOST_LONG_BITS == 64
|
||||
{
|
||||
struct target_dirent *target_dirp;
|
||||
struct linux_dirent *dirp;
|
||||
abi_long count = arg3;
|
||||
|
||||
dirp = g_try_malloc(count);
|
||||
if (!dirp) {
|
||||
return -TARGET_ENOMEM;
|
||||
}
|
||||
|
||||
ret = get_errno(sys_getdents(arg1, dirp, count));
|
||||
if (!is_error(ret)) {
|
||||
struct linux_dirent *de;
|
||||
struct target_dirent *tde;
|
||||
int len = ret;
|
||||
int reclen, treclen;
|
||||
int count1, tnamelen;
|
||||
|
||||
count1 = 0;
|
||||
de = dirp;
|
||||
if (!(target_dirp = lock_user(VERIFY_WRITE, arg2, count, 0)))
|
||||
return -TARGET_EFAULT;
|
||||
tde = target_dirp;
|
||||
while (len > 0) {
|
||||
reclen = de->d_reclen;
|
||||
tnamelen = reclen - offsetof(struct linux_dirent, d_name);
|
||||
assert(tnamelen >= 0);
|
||||
treclen = tnamelen + offsetof(struct target_dirent, d_name);
|
||||
assert(count1 + treclen <= count);
|
||||
tde->d_reclen = tswap16(treclen);
|
||||
tde->d_ino = tswapal(de->d_ino);
|
||||
tde->d_off = tswapal(de->d_off);
|
||||
memcpy(tde->d_name, de->d_name, tnamelen);
|
||||
de = (struct linux_dirent *)((char *)de + reclen);
|
||||
len -= reclen;
|
||||
tde = (struct target_dirent *)((char *)tde + treclen);
|
||||
count1 += treclen;
|
||||
}
|
||||
ret = count1;
|
||||
unlock_user(target_dirp, arg2, ret);
|
||||
}
|
||||
g_free(dirp);
|
||||
}
|
||||
#else
|
||||
{
|
||||
struct linux_dirent *dirp;
|
||||
abi_long count = arg3;
|
||||
|
||||
if (!(dirp = lock_user(VERIFY_WRITE, arg2, count, 0)))
|
||||
return -TARGET_EFAULT;
|
||||
ret = get_errno(sys_getdents(arg1, dirp, count));
|
||||
if (!is_error(ret)) {
|
||||
struct linux_dirent *de;
|
||||
int len = ret;
|
||||
int reclen;
|
||||
de = dirp;
|
||||
while (len > 0) {
|
||||
reclen = de->d_reclen;
|
||||
if (reclen > len)
|
||||
break;
|
||||
de->d_reclen = tswap16(reclen);
|
||||
tswapls(&de->d_ino);
|
||||
tswapls(&de->d_off);
|
||||
de = (struct linux_dirent *)((char *)de + reclen);
|
||||
len -= reclen;
|
||||
}
|
||||
}
|
||||
unlock_user(dirp, arg2, ret);
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
/* Implement getdents in terms of getdents64 */
|
||||
{
|
||||
struct linux_dirent64 *dirp;
|
||||
abi_long count = arg3;
|
||||
|
||||
dirp = lock_user(VERIFY_WRITE, arg2, count, 0);
|
||||
if (!dirp) {
|
||||
return -TARGET_EFAULT;
|
||||
}
|
||||
ret = get_errno(sys_getdents64(arg1, dirp, count));
|
||||
if (!is_error(ret)) {
|
||||
/* Convert the dirent64 structs to target dirent. We do this
|
||||
* in-place, since we can guarantee that a target_dirent is no
|
||||
* larger than a dirent64; however this means we have to be
|
||||
* careful to read everything before writing in the new format.
|
||||
*/
|
||||
struct linux_dirent64 *de;
|
||||
struct target_dirent *tde;
|
||||
int len = ret;
|
||||
int tlen = 0;
|
||||
|
||||
de = dirp;
|
||||
tde = (struct target_dirent *)dirp;
|
||||
while (len > 0) {
|
||||
int namelen, treclen;
|
||||
int reclen = de->d_reclen;
|
||||
uint64_t ino = de->d_ino;
|
||||
int64_t off = de->d_off;
|
||||
uint8_t type = de->d_type;
|
||||
|
||||
namelen = strlen(de->d_name);
|
||||
treclen = offsetof(struct target_dirent, d_name)
|
||||
+ namelen + 2;
|
||||
treclen = QEMU_ALIGN_UP(treclen, sizeof(abi_long));
|
||||
|
||||
memmove(tde->d_name, de->d_name, namelen + 1);
|
||||
tde->d_ino = tswapal(ino);
|
||||
tde->d_off = tswapal(off);
|
||||
tde->d_reclen = tswap16(treclen);
|
||||
/* The target_dirent type is in what was formerly a padding
|
||||
* byte at the end of the structure:
|
||||
*/
|
||||
*(((char *)tde) + treclen - 1) = type;
|
||||
|
||||
de = (struct linux_dirent64 *)((char *)de + reclen);
|
||||
tde = (struct target_dirent *)((char *)tde + treclen);
|
||||
len -= reclen;
|
||||
tlen += treclen;
|
||||
}
|
||||
ret = tlen;
|
||||
}
|
||||
unlock_user(dirp, arg2, ret);
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
return do_getdents(arg1, arg2, arg3);
|
||||
#endif /* TARGET_NR_getdents */
|
||||
#if defined(TARGET_NR_getdents64) && defined(__NR_getdents64)
|
||||
case TARGET_NR_getdents64:
|
||||
{
|
||||
struct linux_dirent64 *dirp;
|
||||
abi_long count = arg3;
|
||||
if (!(dirp = lock_user(VERIFY_WRITE, arg2, count, 0)))
|
||||
return -TARGET_EFAULT;
|
||||
ret = get_errno(sys_getdents64(arg1, dirp, count));
|
||||
if (!is_error(ret)) {
|
||||
struct linux_dirent64 *de;
|
||||
int len = ret;
|
||||
int reclen;
|
||||
de = dirp;
|
||||
while (len > 0) {
|
||||
reclen = de->d_reclen;
|
||||
if (reclen > len)
|
||||
break;
|
||||
de->d_reclen = tswap16(reclen);
|
||||
tswap64s((uint64_t *)&de->d_ino);
|
||||
tswap64s((uint64_t *)&de->d_off);
|
||||
de = (struct linux_dirent64 *)((char *)de + reclen);
|
||||
len -= reclen;
|
||||
}
|
||||
}
|
||||
unlock_user(dirp, arg2, ret);
|
||||
}
|
||||
return ret;
|
||||
return do_getdents64(arg1, arg2, arg3);
|
||||
#endif /* TARGET_NR_getdents64 */
|
||||
#if defined(TARGET_NR__newselect)
|
||||
case TARGET_NR__newselect:
|
||||
|
Loading…
Reference in New Issue
Block a user