diff --git a/src/kernel/emulation/linux/dirent/getdirentries.c b/src/kernel/emulation/linux/dirent/getdirentries.c index cb2940085..0faaa5273 100644 --- a/src/kernel/emulation/linux/dirent/getdirentries.c +++ b/src/kernel/emulation/linux/dirent/getdirentries.c @@ -7,6 +7,20 @@ extern unsigned long strlen(const char* s); extern char* strcpy(char* dest, const char* src); +#ifndef min +# define min(a,b) ((a) < (b)) ? (a) : (b) +#endif + +static inline void round_to_4(unsigned short* reclen) +{ + if (*reclen & 3) + { + *reclen += 3; + *reclen /= 4; + *reclen *= 4; + } +} + long sys_getdirentries(int fd, char* ibuf, unsigned int len, long* basep) { int ret, bpos = 0, opos = 0; @@ -15,28 +29,30 @@ long sys_getdirentries(int fd, char* ibuf, unsigned int len, long* basep) if (basep) *basep = 0; - ret = LINUX_SYSCALL(__NR_getdents64, fd, buf, sizeof(buf)); + ret = LINUX_SYSCALL(__NR_getdents64, fd, buf, min(len, sizeof(buf))); if (ret < 0) return errno_linux_to_bsd(ret); while (bpos < ret && opos < len) { struct linux_dirent64* l64; - struct dirent* bsd; - int len; + struct bsd_dirent* bsd; + int slen; l64 = (struct linux_dirent64*) (buf + bpos); - bsd = (struct dirent*) (ibuf + opos); - len = strlen(l64->d_name); + bsd = (struct bsd_dirent*) (ibuf + opos); + slen = strlen(l64->d_name); - if (opos-len < sizeof(struct dirent) + len + 1) + if (len-opos < sizeof(struct bsd_dirent) + slen + 1) break; bsd->d_ino = l64->d_ino; bsd->d_type = l64->d_type; strcpy(bsd->d_name, l64->d_name); - bsd->d_reclen = sizeof(struct dirent) + len + 1; - bsd->d_namlen = len; + bsd->d_reclen = sizeof(struct bsd_dirent) + slen + 1; + bsd->d_namlen = slen; + + round_to_4(&bsd->d_reclen); opos += bsd->d_reclen; bpos += l64->d_reclen; @@ -55,30 +71,32 @@ long sys_getdirentries64(int fd, char* ibuf, unsigned int len, long* basep) if (basep) *basep = 0; - ret = LINUX_SYSCALL(__NR_getdents64, fd, buf, sizeof(buf)); + ret = LINUX_SYSCALL(__NR_getdents64, fd, buf, min(len, sizeof(buf))); if (ret < 0) return errno_linux_to_bsd(ret); while (bpos < ret && opos < len) { struct linux_dirent64* l64; - struct dirent64* bsd; - int len; + struct bsd_dirent64* bsd; + int slen; l64 = (struct linux_dirent64*) (buf + bpos); - bsd = (struct dirent64*) (ibuf + opos); - len = strlen(l64->d_name); + bsd = (struct bsd_dirent64*) (ibuf + opos); + slen = strlen(l64->d_name); - if (opos-len < sizeof(struct dirent) + len + 1) + if (len-opos < sizeof(struct bsd_dirent64) + slen + 1) break; bsd->d_ino = l64->d_ino; bsd->d_type = l64->d_type; strcpy(bsd->d_name, l64->d_name); - bsd->d_reclen = sizeof(struct dirent) + len + 1; - bsd->d_namlen = len; + bsd->d_reclen = sizeof(struct bsd_dirent64) + slen + 1; + bsd->d_namlen = slen; bsd->d_seekoff = 0; + round_to_4(&bsd->d_reclen); + opos += bsd->d_reclen; bpos += l64->d_reclen; } diff --git a/src/kernel/emulation/linux/dirent/getdirentries.h b/src/kernel/emulation/linux/dirent/getdirentries.h index a8c83b83c..5fee38c36 100644 --- a/src/kernel/emulation/linux/dirent/getdirentries.h +++ b/src/kernel/emulation/linux/dirent/getdirentries.h @@ -13,5 +13,24 @@ struct linux_dirent64 char d_name[]; }; +struct bsd_dirent +{ + unsigned int d_ino; + unsigned short d_reclen; + unsigned char d_type; + unsigned char d_namlen; + char d_name[]; +}; + +struct bsd_dirent64 +{ + unsigned long long d_ino; + unsigned long long d_seekoff; + unsigned short d_reclen; + unsigned short d_namlen; + unsigned char d_type; + char d_name[]; +}; + #endif