summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorrdivacky <rdivacky@FreeBSD.org>2008-09-09 16:00:17 +0000
committerrdivacky <rdivacky@FreeBSD.org>2008-09-09 16:00:17 +0000
commit817c519713d426e0159692b3c5ffa2d36325a5ad (patch)
treee54fea4d864cc2015b0c9912653035b7e62b9cec /sys
parent73a287b49184cc4b7cc4c689ada9fd5cb5ccc884 (diff)
downloadFreeBSD-src-817c519713d426e0159692b3c5ffa2d36325a5ad.zip
FreeBSD-src-817c519713d426e0159692b3c5ffa2d36325a5ad.tar.gz
Getdents requires padding with 2 bytes instead of 1 byte
as with getdents64. The last byte is used for storing the d_type, add this to plain getdents case where it was missing before. Also change the code to use strlcpy instead of plain strcpy. This changes fix the getdents crash we had reports about (hl2 server etc.) PR: kern/117010 MFC after: 1 week Submitted by: Dmitry Chagin (dchagin@) Tested by: MITA Yoshio <mita ee.t.u-tokyo.ac jp> Approved by: kib (mentor)
Diffstat (limited to 'sys')
-rw-r--r--sys/compat/linux/linux_file.c87
1 files changed, 54 insertions, 33 deletions
diff --git a/sys/compat/linux/linux_file.c b/sys/compat/linux/linux_file.c
index dafdb3a..6643e71 100644
--- a/sys/compat/linux/linux_file.c
+++ b/sys/compat/linux/linux_file.c
@@ -303,9 +303,20 @@ struct l_dirent64 {
char d_name[LINUX_NAME_MAX + 1];
};
-#define LINUX_RECLEN(de,namlen) \
- ALIGN((((char *)&(de)->d_name - (char *)de) + (namlen) + 1))
+/*
+ * Linux uses the last byte in the dirent buffer to store d_type,
+ * at least glibc-2.7 requires it. That is why l_dirent is padded with 2 bytes.
+ */
+#define LINUX_RECLEN(namlen) \
+ roundup((offsetof(struct l_dirent, d_name) + (namlen) + 2), \
+ sizeof(l_ulong))
+
+#define LINUX_RECLEN64(namlen) \
+ roundup((offsetof(struct l_dirent64, d_name) + (namlen) + 1), \
+ sizeof(uint64_t))
+#define LINUX_MAXRECLEN max(LINUX_RECLEN(LINUX_NAME_MAX), \
+ LINUX_RECLEN64(LINUX_NAME_MAX))
#define LINUX_DIRBLKSIZ 512
static int
@@ -318,12 +329,13 @@ getdents_common(struct thread *td, struct linux_getdents64_args *args,
int len, reclen; /* BSD-format */
caddr_t outp; /* Linux-format */
int resid, linuxreclen=0; /* Linux-format */
+ caddr_t lbuf; /* Linux-format */
struct file *fp;
struct uio auio;
struct iovec aiov;
off_t off;
- struct l_dirent linux_dirent;
- struct l_dirent64 linux_dirent64;
+ struct l_dirent *linux_dirent;
+ struct l_dirent64 *linux_dirent64;
int buflen, error, eofflag, nbytes, justone;
u_long *cookies = NULL, *cookiep;
int ncookies, vfslocked;
@@ -359,6 +371,7 @@ getdents_common(struct thread *td, struct linux_getdents64_args *args,
buflen = max(LINUX_DIRBLKSIZ, nbytes);
buflen = min(buflen, MAXBSIZE);
buf = malloc(buflen, M_TEMP, M_WAITOK);
+ lbuf = malloc(LINUX_MAXRECLEN, M_TEMP, M_WAITOK | M_ZERO);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
again:
@@ -436,8 +449,8 @@ again:
}
linuxreclen = (is64bit)
- ? LINUX_RECLEN(&linux_dirent64, bdp->d_namlen)
- : LINUX_RECLEN(&linux_dirent, bdp->d_namlen);
+ ? LINUX_RECLEN64(bdp->d_namlen)
+ : LINUX_RECLEN(bdp->d_namlen);
if (reclen > len || resid < linuxreclen) {
outp++;
@@ -446,34 +459,41 @@ again:
if (justone) {
/* readdir(2) case. */
- linux_dirent.d_ino = bdp->d_fileno;
- linux_dirent.d_off = (l_off_t)linuxreclen;
- linux_dirent.d_reclen = (l_ushort)bdp->d_namlen;
- strcpy(linux_dirent.d_name, bdp->d_name);
- error = copyout(&linux_dirent, outp, linuxreclen);
- } else {
- if (is64bit) {
- linux_dirent64.d_ino = bdp->d_fileno;
- linux_dirent64.d_off = (cookiep)
- ? (l_off_t)*cookiep
- : (l_off_t)(off + reclen);
- linux_dirent64.d_reclen =
- (l_ushort)linuxreclen;
- linux_dirent64.d_type = bdp->d_type;
- strcpy(linux_dirent64.d_name, bdp->d_name);
- error = copyout(&linux_dirent64, outp,
- linuxreclen);
- } else {
- linux_dirent.d_ino = bdp->d_fileno;
- linux_dirent.d_off = (cookiep)
- ? (l_off_t)*cookiep
- : (l_off_t)(off + reclen);
- linux_dirent.d_reclen = (l_ushort)linuxreclen;
- strcpy(linux_dirent.d_name, bdp->d_name);
- error = copyout(&linux_dirent, outp,
- linuxreclen);
- }
+ linux_dirent = (struct l_dirent*)lbuf;
+ linux_dirent->d_ino = bdp->d_fileno;
+ linux_dirent->d_off = (l_off_t)linuxreclen;
+ linux_dirent->d_reclen = (l_ushort)bdp->d_namlen;
+ strlcpy(linux_dirent->d_name, bdp->d_name,
+ linuxreclen - offsetof(struct l_dirent, d_name));
+ error = copyout(linux_dirent, outp, linuxreclen);
}
+ if (is64bit) {
+ linux_dirent64 = (struct l_dirent64*)lbuf;
+ linux_dirent64->d_ino = bdp->d_fileno;
+ linux_dirent64->d_off = (cookiep)
+ ? (l_off_t)*cookiep
+ : (l_off_t)(off + reclen);
+ linux_dirent64->d_reclen = (l_ushort)linuxreclen;
+ linux_dirent64->d_type = bdp->d_type;
+ strlcpy(linux_dirent64->d_name, bdp->d_name,
+ linuxreclen - offsetof(struct l_dirent64, d_name));
+ error = copyout(linux_dirent64, outp, linuxreclen);
+ } else if (!justone) {
+ linux_dirent = (struct l_dirent*)lbuf;
+ linux_dirent->d_ino = bdp->d_fileno;
+ linux_dirent->d_off = (cookiep)
+ ? (l_off_t)*cookiep
+ : (l_off_t)(off + reclen);
+ linux_dirent->d_reclen = (l_ushort)linuxreclen;
+ /*
+ * Copy d_type to last byte of l_dirent buffer
+ */
+ lbuf[linuxreclen-1] = bdp->d_type;
+ strlcpy(linux_dirent->d_name, bdp->d_name,
+ linuxreclen - offsetof(struct l_dirent, d_name)-1);
+ error = copyout(linux_dirent, outp, linuxreclen);
+ }
+
if (error)
goto out;
@@ -509,6 +529,7 @@ out:
VFS_UNLOCK_GIANT(vfslocked);
fdrop(fp, td);
free(buf, M_TEMP);
+ free(lbuf, M_TEMP);
return (error);
}
OpenPOWER on IntegriCloud