diff options
Diffstat (limited to 'sys/fs/tmpfs/tmpfs.h')
-rw-r--r-- | sys/fs/tmpfs/tmpfs.h | 71 |
1 files changed, 67 insertions, 4 deletions
diff --git a/sys/fs/tmpfs/tmpfs.h b/sys/fs/tmpfs/tmpfs.h index c57eb25..b623514 100644 --- a/sys/fs/tmpfs/tmpfs.h +++ b/sys/fs/tmpfs/tmpfs.h @@ -1,7 +1,7 @@ -/* $NetBSD: tmpfs.h,v 1.18 2006/03/31 20:27:49 riz Exp $ */ +/* $NetBSD: tmpfs.h,v 1.26 2007/02/22 06:37:00 thorpej Exp $ */ /* - * Copyright (c) 2005 The NetBSD Foundation, Inc. + * Copyright (c) 2005, 2006 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -97,10 +97,73 @@ struct tmpfs_dirent { * importantly, to remove redundancy. */ TAILQ_HEAD(tmpfs_dir, tmpfs_dirent); -#define TMPFS_DIRCOOKIE(dirent) ((off_t)(uintptr_t)(dirent)) +/* Each entry in a directory has a cookie that identifies it. Cookies + * supersede offsets within directories because, given how tmpfs stores + * directories in memory, there is no such thing as an offset. (Emulating + * a real offset could be very difficult.) + * + * The '.', '..' and the end of directory markers have fixed cookies which + * cannot collide with the cookies generated by other entries. The cookies + * fot the other entries are generated based on the memory address on which + * stores their information is stored. + * + * Ideally, using the entry's memory pointer as the cookie would be enough + * to represent it and it wouldn't cause collisions in any system. + * Unfortunately, this results in "offsets" with very large values which + * later raise problems in the Linux compatibility layer (and maybe in other + * places) as described in PR kern/32034. Hence we need to workaround this + * with a rather ugly hack. + * + * Linux 32-bit binaries, unless built with _FILE_OFFSET_BITS=64, have off_t + * set to 'long', which is a 32-bit *signed* long integer. Regardless of + * the macro value, GLIBC (2.3 at least) always uses the getdents64 + * system call (when calling readdir) which internally returns off64_t + * offsets. In order to make 32-bit binaries work, *GLIBC* converts the + * 64-bit values returned by the kernel to 32-bit ones and aborts with + * EOVERFLOW if the conversion results in values that won't fit in 32-bit + * integers (which it assumes is because the directory is extremely large). + * This wouldn't cause problems if we were dealing with unsigned integers, + * but as we have signed integers, this check fails due to sign expansion. + * + * For example, consider that the kernel returns the 0xc1234567 cookie to + * userspace in a off64_t integer. Later on, GLIBC casts this value to + * off_t (remember, signed) with code similar to: + * system call returns the offset in kernel_value; + * off_t casted_value = kernel_value; + * if (sizeof(off_t) != sizeof(off64_t) && + * kernel_value != casted_value) + * error! + * In this case, casted_value still has 0xc1234567, but when it is compared + * for equality against kernel_value, it is promoted to a 64-bit integer and + * becomes 0xffffffffc1234567, which is different than 0x00000000c1234567. + * Then, GLIBC assumes this is because the directory is very large. + * + * Given that all the above happens in user-space, we have no control over + * it; therefore we must workaround the issue here. We do this by + * truncating the pointer value to a 32-bit integer and hope that there + * won't be collisions. In fact, this will not cause any problems in + * 32-bit platforms but some might arise in 64-bit machines (I'm not sure + * if they can happen at all in practice). + * + * XXX A nicer solution shall be attempted. */ +#ifdef _KERNEL #define TMPFS_DIRCOOKIE_DOT 0 #define TMPFS_DIRCOOKIE_DOTDOT 1 #define TMPFS_DIRCOOKIE_EOF 2 +static __inline +off_t +tmpfs_dircookie(struct tmpfs_dirent *de) +{ + off_t cookie; + + cookie = ((off_t)(uintptr_t)de >> 1) & 0x7FFFFFFF; + MPASS(cookie != TMPFS_DIRCOOKIE_DOT); + MPASS(cookie != TMPFS_DIRCOOKIE_DOTDOT); + MPASS(cookie != TMPFS_DIRCOOKIE_EOF); + + return cookie; +} +#endif /* --------------------------------------------------------------------- */ @@ -408,7 +471,7 @@ int tmpfs_truncate(struct vnode *, off_t); MPASS((node)->tn_type == VDIR); \ MPASS((node)->tn_size % sizeof(struct tmpfs_dirent) == 0); \ MPASS((node)->tn_dir.tn_readdir_lastp == NULL || \ - TMPFS_DIRCOOKIE((node)->tn_dir.tn_readdir_lastp) == (node)->tn_dir.tn_readdir_lastn); + tmpfs_dircookie((node)->tn_dir.tn_readdir_lastp) == (node)->tn_dir.tn_readdir_lastn); /* --------------------------------------------------------------------- */ |