diff options
author | delphij <delphij@FreeBSD.org> | 2007-08-10 11:00:30 +0000 |
---|---|---|
committer | delphij <delphij@FreeBSD.org> | 2007-08-10 11:00:30 +0000 |
commit | 54967434093c09d04d57a4a94ba75d232e82d375 (patch) | |
tree | b2c92a38ae0ffd170371166c775ddb77348fb7e8 /sys/fs/tmpfs/tmpfs.h | |
parent | f0f8d2f251df46a5b0ae281920fcbcc216664a9b (diff) | |
download | FreeBSD-src-54967434093c09d04d57a4a94ba75d232e82d375.zip FreeBSD-src-54967434093c09d04d57a4a94ba75d232e82d375.tar.gz |
MFp4:
- LK_RETRY prohibits vget() and vn_lock() to return error.
Remove associated code. [1]
- Properly use vhold() and vdrop() instead of their unlocked
versions, we are guaranteed to have the vnode's interlock
unheld. [1]
- Fix a pseudo-infinite loop caused by 64/32-bit arithmetic
with the same way used in modern NetBSD versions. [2]
- Reorganize tmpfs_readdir to reduce duplicated code.
Submitted by: kib [1]
Obtained from: NetBSD [2]
Approved by: re (tmpfs blanket)
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); /* --------------------------------------------------------------------- */ |