aboutsummaryrefslogtreecommitdiffstats
path: root/module/zfs/zfs_vnops.c
diff options
context:
space:
mode:
authorRichard Yao <[email protected]>2013-10-02 11:22:53 -0400
committerBrian Behlendorf <[email protected]>2013-10-29 09:51:59 -0700
commitc12e3a594a49ed10b7870d950c1f336f78f136cb (patch)
treef44342977c62b07bffa357b53a2afb804fd91ffc /module/zfs/zfs_vnops.c
parentd65e73810938e5619b72591d3438063b00949e77 (diff)
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 8170d281263e52ff33d7fba93ab625196844df36. 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 <[email protected]> Signed-off-by: Brian Behlendorf <[email protected]> Closes #1750
Diffstat (limited to 'module/zfs/zfs_vnops.c')
-rw-r--r--module/zfs/zfs_vnops.c37
1 files changed, 22 insertions, 15 deletions
diff --git a/module/zfs/zfs_vnops.c b/module/zfs/zfs_vnops.c
index 876d44b35..436a25eaf 100644
--- a/module/zfs/zfs_vnops.c
+++ b/module/zfs/zfs_vnops.c
@@ -2004,12 +2004,12 @@ zfs_readdir(struct inode *ip, struct dir_context *ctx, cred_t *cr)
objset_t *os;
zap_cursor_t zc;
zap_attribute_t zap;
- int outcount;
int error;
uint8_t prefetch;
+ uint8_t type;
int done = 0;
uint64_t parent;
- loff_t *pos = &(ctx->pos);
+ uint64_t offset; /* must be unsigned; checks for < 1 */
ZFS_ENTER(zsb);
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)
*/
- error = 0;
if (zp->z_unlinked)
goto out;
+ error = 0;
os = zsb->z_os;
+ offset = ctx->pos;
prefetch = zp->z_zn_prefetch;
/*
* Initialize the iterator cursor.
*/
- if (*pos <= 3) {
+ if (offset <= 3) {
/*
* 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.
*/
- 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
*/
- outcount = 0;
-
while (!done) {
uint64_t objnum;
/*
* Special case `.', `..', and `.zfs'.
*/
- if (*pos == 0) {
+ if (offset == 0) {
(void) strcpy(zap.za_name, ".");
zap.za_normalization_conflict = 0;
objnum = zp->z_id;
- } else if (*pos == 1) {
+ type = DT_DIR;
+ } else if (offset == 1) {
(void) strcpy(zap.za_name, "..");
zap.za_normalization_conflict = 0;
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);
zap.za_normalization_conflict = 0;
objnum = ZFSCTL_INO_ROOT;
+ type = DT_DIR;
} else {
/*
* Grab next entry.
@@ -2089,7 +2091,7 @@ zfs_readdir(struct inode *ip, struct dir_context *ctx, cred_t *cr)
"entry, obj = %lld, offset = %lld, "
"length = %d, num = %lld\n",
(u_longlong_t)zp->z_id,
- (u_longlong_t)*pos,
+ (u_longlong_t)offset,
zap.za_integer_length,
(u_longlong_t)zap.za_num_integers);
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);
+ type = ZFS_DIRENT_TYPE(zap.za_first_integer);
}
done = !dir_emit(ctx, zap.za_name, strlen(zap.za_name),
- objnum, ZFS_DIRENT_TYPE(zap.za_first_integer));
+ objnum, type);
if (done)
break;
@@ -2109,12 +2112,16 @@ zfs_readdir(struct inode *ip, struct dir_context *ctx, cred_t *cr)
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);
- *pos = zap_cursor_serialize(&zc);
+ offset = zap_cursor_serialize(&zc);
} else {
- (*pos)++;
+ offset += 1;
}
+ ctx->pos = offset;
}
zp->z_zn_prefetch = B_FALSE; /* a lookup will re-enable pre-fetching */