summaryrefslogtreecommitdiffstats
path: root/xlators
diff options
context:
space:
mode:
authorEmmanuel Dreyfus <manu@netbsd.org>2014-09-29 03:06:24 +0200
committerVijay Bellur <vbellur@redhat.com>2014-09-30 09:40:36 -0700
commitc65d4ea8a10a4004cab145aaea0362e03b209267 (patch)
tree9ee15019ebaca48c46ffd7af4cbf5efb50a1d242 /xlators
parent36d2975714ed6ef98c0f86a2fac22fc382ea8a9d (diff)
Fix invalid seekdir() usage
According to POSIX, seekdir() should only be given offset obtained from telldir() on the same DIR * http://pubs.opengroup.org/onlinepubs/9699919799/functions/seekdir.html Code from afr-self-heald.c and index.c is operating outside of the specification, by doing using seekdir() with offset from a previously open/close/re-open directory. This seems to work on Linux (although with no guarantee it will always in the future). On NetBSD the seekdir() with a in invalid offset is a nilpotent operation, and causes an infinite loop, since index_fill_readdir() always restart from the beginning of the directory. The situation is fixed by using a non anonymous fd in afr-self-heald.c: we explicitely open the directory so that it remains open on the brick side during the timeframe where we want to reuse offsets in seekdir(). This requires adding an opendir fop in index xlator. If the brick was not updated, the opendir will fail and we fallback to the standard violating approach for backward compatibility on Linux. On other systems we fail since it never worked. While there, add tests to check seekdir() success in index and posix xlators, so that incorrect usage from calling code produce an explicit error instead of an infinite loop. We can only do it on non Linux systems, for the sake of backward compatibility when the brick was updated but not the client. BUG: 1129939 Change-Id: I88ca90acfcfee280988124bd6addc1a1893ca7ab Signed-off-by: Emmanuel Dreyfus <manu@netbsd.org> Reviewed-on: http://review.gluster.org/8760 Tested-by: Gluster Build System <jenkins@build.gluster.com> Reviewed-by: Pranith Kumar Karampuri <pkarampu@redhat.com> Reviewed-by: Vijay Bellur <vbellur@redhat.com>
Diffstat (limited to 'xlators')
-rw-r--r--xlators/cluster/afr/src/afr-self-heald.c39
-rw-r--r--xlators/features/index/src/index.c44
-rw-r--r--xlators/storage/posix/src/posix.c23
3 files changed, 103 insertions, 3 deletions
diff --git a/xlators/cluster/afr/src/afr-self-heald.c b/xlators/cluster/afr/src/afr-self-heald.c
index 9627b906b56..10e19964ed9 100644
--- a/xlators/cluster/afr/src/afr-self-heald.c
+++ b/xlators/cluster/afr/src/afr-self-heald.c
@@ -229,6 +229,7 @@ afr_shd_index_opendir (xlator_t *this, int child)
int ret = 0;
dict_t *xattr = NULL;
void *index_gfid = NULL;
+ loc_t loc = {0, };
priv = this->private;
subvol = priv->children[child];
@@ -253,11 +254,43 @@ afr_shd_index_opendir (xlator_t *this, int child)
inode = afr_shd_inode_find (this, subvol, index_gfid);
if (!inode)
goto out;
- fd = fd_anonymous (inode);
+
+ fd = fd_create (inode, GF_CLIENT_PID_AFR_SELF_HEALD);
+ if (!fd)
+ goto out;
+
+ uuid_copy (loc.gfid, index_gfid);
+ loc.inode = inode;
+
+ ret = syncop_opendir(this, &loc, fd);
+ if (ret) {
+ /*
+ * On Linux, if the brick was not updated, opendir will
+ * fail. We therefore use backward compatible code
+ * that violate the standards by reusing offsets
+ * in seekdir() from different DIR *, but it works on Linux.
+ *
+ * On other systems it never worked, hence we do not need
+ * to provide backward-compatibility.
+ */
+#ifdef GF_LINUX_HOST_OS
+ fd_unref (fd);
+ fd = fd_anonymous (inode);
+#else /* GF_LINUX_HOST_OS */
+ gf_log(this->name, GF_LOG_ERROR,
+ "opendir of %s for %s failed: %s",
+ uuid_utoa (index_gfid), subvol->name, strerror(errno));
+ fd_unref (fd);
+ fd = NULL;
+ goto out;
+#endif /* GF_LINUX_HOST_OS */
+ }
+
out:
loc_wipe (&rootloc);
- if (inode)
- inode_unref (inode);
+
+ if (inode)
+ inode_unref (inode);
if (xattr)
dict_unref (xattr);
diff --git a/xlators/features/index/src/index.c b/xlators/features/index/src/index.c
index 2b80e718607..c342da8274c 100644
--- a/xlators/features/index/src/index.c
+++ b/xlators/features/index/src/index.c
@@ -285,6 +285,17 @@ index_fill_readdir (fd_t *fd, DIR *dir, off_t off,
rewinddir (dir);
} else {
seekdir (dir, off);
+#ifndef GF_LINUX_HOST_OS
+ if (telldir(dir) != off) {
+ gf_log (THIS->name, GF_LOG_ERROR,
+ "seekdir(%ld) failed on dir=%p: "
+ "Invalid argument (offset reused from "
+ "another DIR * structure?)", (long)off, dir);
+ errno = EINVAL;
+ count = -1;
+ goto out;
+ }
+#endif /* GF_LINUX_HOST_OS */
}
while (filled <= size) {
@@ -323,6 +334,18 @@ index_fill_readdir (fd_t *fd, DIR *dir, off_t off,
if (this_size + filled > size) {
seekdir (dir, in_case);
+#ifndef GF_LINUX_HOST_OS
+ if (telldir(dir) != in_case) {
+ gf_log (THIS->name, GF_LOG_ERROR,
+ "seekdir(%ld) failed on dir=%p: "
+ "Invalid argument (offset reused from "
+ "another DIR * structure?)",
+ (long)in_case, dir);
+ errno = EINVAL;
+ count = -1;
+ goto out;
+ }
+#endif /* GF_LINUX_HOST_OS */
break;
}
@@ -1043,6 +1066,26 @@ normal:
}
int32_t
+index_opendir (call_frame_t *frame, xlator_t *this,
+ loc_t *loc, fd_t *fd, dict_t *xdata)
+{
+ index_priv_t *priv = NULL;
+
+ priv = this->private;
+ if (uuid_compare (fd->inode->gfid, priv->xattrop_vgfid))
+ goto normal;
+
+ frame->local = NULL;
+ STACK_UNWIND_STRICT (opendir, frame, 0, 0, fd, NULL);
+ return 0;
+
+normal:
+ STACK_WIND (frame, default_opendir_cbk, FIRST_CHILD(this),
+ FIRST_CHILD(this)->fops->opendir, loc, fd, xdata);
+ return 0;
+}
+
+int32_t
index_readdir (call_frame_t *frame, xlator_t *this,
fd_t *fd, size_t size, off_t off, dict_t *xdata)
{
@@ -1267,6 +1310,7 @@ struct xlator_fops fops = {
//interface functions follow
.getxattr = index_getxattr,
.lookup = index_lookup,
+ .opendir = index_opendir,
.readdir = index_readdir,
.unlink = index_unlink
};
diff --git a/xlators/storage/posix/src/posix.c b/xlators/storage/posix/src/posix.c
index 43bd3fc1cfd..e830a973b76 100644
--- a/xlators/storage/posix/src/posix.c
+++ b/xlators/storage/posix/src/posix.c
@@ -4842,6 +4842,17 @@ posix_fill_readdir (fd_t *fd, DIR *dir, off_t off, size_t size,
rewinddir (dir);
} else {
seekdir (dir, off);
+#ifndef GF_LINUX_HOST_OS
+ if (telldir(dir) != off) {
+ gf_log (THIS->name, GF_LOG_ERROR,
+ "seekdir(%ld) failed on dir=%p: "
+ "Invalid argument (offset reused from "
+ "another DIR * structure?)", (long)off, dir);
+ errno = EINVAL;
+ count = -1;
+ goto out;
+ }
+#endif /* GF_LINUX_HOST_OS */
}
while (filled <= size) {
@@ -4904,6 +4915,18 @@ posix_fill_readdir (fd_t *fd, DIR *dir, off_t off, size_t size,
if (this_size + filled > size) {
seekdir (dir, in_case);
+#ifndef GF_LINUX_HOST_OS
+ if (telldir(dir) != in_case) {
+ gf_log (THIS->name, GF_LOG_ERROR,
+ "seekdir(%ld) failed on dir=%p: "
+ "Invalid argument (offset reused from "
+ "another DIR * structure?)",
+ (long)in_case, dir);
+ errno = EINVAL;
+ count = -1;
+ goto out;
+ }
+#endif /* GF_LINUX_HOST_OS */
break;
}