Restructure zfs_readdir() to fix regressions

This does the following:

1. It creates a uint8_t type value, which is initialized to DT_DIR on
dot directories and ZFS_DIRENT_TYPE(zap.za_first_integer) otherwise.
This resolves a regression where we return unintialized values as the
directory entry type on dot directories. This was accidentally
introduced by commit 8170d28126.

2. It restructures zfs_readdir() code to use `uint64_t offset` like
Illumos instead of `loff_t *pos`. This resolves a regression where
negative ZAP cursors were treated as if they were dot directories.

3. It restructures the function to more closely match the structure of
zfs_readdir() on Illumos and removes the unused variable outcount, which
was only used on Illumos.

Signed-off-by: Richard Yao <ryao@gentoo.org>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1750
This commit is contained in:
Richard Yao 2013-10-02 11:22:53 -04:00 committed by Brian Behlendorf
parent d65e738109
commit c12e3a594a

View File

@ -2004,12 +2004,12 @@ zfs_readdir(struct inode *ip, struct dir_context *ctx, cred_t *cr)
objset_t *os; objset_t *os;
zap_cursor_t zc; zap_cursor_t zc;
zap_attribute_t zap; zap_attribute_t zap;
int outcount;
int error; int error;
uint8_t prefetch; uint8_t prefetch;
uint8_t type;
int done = 0; int done = 0;
uint64_t parent; uint64_t parent;
loff_t *pos = &(ctx->pos); uint64_t offset; /* must be unsigned; checks for < 1 */
ZFS_ENTER(zsb); ZFS_ENTER(zsb);
ZFS_VERIFY_ZP(zp); ZFS_VERIFY_ZP(zp);
@ -2021,17 +2021,18 @@ zfs_readdir(struct inode *ip, struct dir_context *ctx, cred_t *cr)
/* /*
* Quit if directory has been removed (posix) * Quit if directory has been removed (posix)
*/ */
error = 0;
if (zp->z_unlinked) if (zp->z_unlinked)
goto out; goto out;
error = 0;
os = zsb->z_os; os = zsb->z_os;
offset = ctx->pos;
prefetch = zp->z_zn_prefetch; prefetch = zp->z_zn_prefetch;
/* /*
* Initialize the iterator cursor. * Initialize the iterator cursor.
*/ */
if (*pos <= 3) { if (offset <= 3) {
/* /*
* Start iteration from the beginning of the directory. * Start iteration from the beginning of the directory.
*/ */
@ -2040,31 +2041,32 @@ zfs_readdir(struct inode *ip, struct dir_context *ctx, cred_t *cr)
/* /*
* The offset is a serialized cursor. * The offset is a serialized cursor.
*/ */
zap_cursor_init_serialized(&zc, os, zp->z_id, *pos); zap_cursor_init_serialized(&zc, os, zp->z_id, offset);
} }
/* /*
* Transform to file-system independent format * Transform to file-system independent format
*/ */
outcount = 0;
while (!done) { while (!done) {
uint64_t objnum; uint64_t objnum;
/* /*
* Special case `.', `..', and `.zfs'. * Special case `.', `..', and `.zfs'.
*/ */
if (*pos == 0) { if (offset == 0) {
(void) strcpy(zap.za_name, "."); (void) strcpy(zap.za_name, ".");
zap.za_normalization_conflict = 0; zap.za_normalization_conflict = 0;
objnum = zp->z_id; objnum = zp->z_id;
} else if (*pos == 1) { type = DT_DIR;
} else if (offset == 1) {
(void) strcpy(zap.za_name, ".."); (void) strcpy(zap.za_name, "..");
zap.za_normalization_conflict = 0; zap.za_normalization_conflict = 0;
objnum = parent; objnum = parent;
} else if (*pos == 2 && zfs_show_ctldir(zp)) { type = DT_DIR;
} else if (offset == 2 && zfs_show_ctldir(zp)) {
(void) strcpy(zap.za_name, ZFS_CTLDIR_NAME); (void) strcpy(zap.za_name, ZFS_CTLDIR_NAME);
zap.za_normalization_conflict = 0; zap.za_normalization_conflict = 0;
objnum = ZFSCTL_INO_ROOT; objnum = ZFSCTL_INO_ROOT;
type = DT_DIR;
} else { } else {
/* /*
* Grab next entry. * Grab next entry.
@ -2089,7 +2091,7 @@ zfs_readdir(struct inode *ip, struct dir_context *ctx, cred_t *cr)
"entry, obj = %lld, offset = %lld, " "entry, obj = %lld, offset = %lld, "
"length = %d, num = %lld\n", "length = %d, num = %lld\n",
(u_longlong_t)zp->z_id, (u_longlong_t)zp->z_id,
(u_longlong_t)*pos, (u_longlong_t)offset,
zap.za_integer_length, zap.za_integer_length,
(u_longlong_t)zap.za_num_integers); (u_longlong_t)zap.za_num_integers);
error = ENXIO; error = ENXIO;
@ -2097,10 +2099,11 @@ zfs_readdir(struct inode *ip, struct dir_context *ctx, cred_t *cr)
} }
objnum = ZFS_DIRENT_OBJ(zap.za_first_integer); objnum = ZFS_DIRENT_OBJ(zap.za_first_integer);
type = ZFS_DIRENT_TYPE(zap.za_first_integer);
} }
done = !dir_emit(ctx, zap.za_name, strlen(zap.za_name), done = !dir_emit(ctx, zap.za_name, strlen(zap.za_name),
objnum, ZFS_DIRENT_TYPE(zap.za_first_integer)); objnum, type);
if (done) if (done)
break; break;
@ -2109,12 +2112,16 @@ zfs_readdir(struct inode *ip, struct dir_context *ctx, cred_t *cr)
dmu_prefetch(os, objnum, 0, 0); dmu_prefetch(os, objnum, 0, 0);
} }
if (*pos > 2 || (*pos == 2 && !zfs_show_ctldir(zp))) { /*
* Move to the next entry, fill in the previous offset.
*/
if (offset > 2 || (offset == 2 && !zfs_show_ctldir(zp))) {
zap_cursor_advance(&zc); zap_cursor_advance(&zc);
*pos = zap_cursor_serialize(&zc); offset = zap_cursor_serialize(&zc);
} else { } else {
(*pos)++; offset += 1;
} }
ctx->pos = offset;
} }
zp->z_zn_prefetch = B_FALSE; /* a lookup will re-enable pre-fetching */ zp->z_zn_prefetch = B_FALSE; /* a lookup will re-enable pre-fetching */