LASSERT(body);
/* only detect the xattr size */
- if (size == 0)
+ if (size == 0) {
+ /* LU-11109: Older MDTs do not distinguish
+ * between nonexistent xattrs and zero length
+ * values in this case. Newer MDTs will return
+ * -ENODATA or set OBD_MD_FLXATTR. */
GOTO(out, rc = body->mbo_eadatasize);
+ }
if (size < body->mbo_eadatasize) {
CERROR("server bug: replied size %u > %u\n",
GOTO(out, rc = -ERANGE);
}
- if (body->mbo_eadatasize == 0)
- GOTO(out, rc = -ENODATA);
+ if (body->mbo_eadatasize == 0) {
+ /* LU-11109: Newer MDTs set OBD_MD_FLXATTR on
+ * success so that we can distinguish between
+ * zero length value and nonexistent xattr.
+ *
+ * If OBD_MD_FLXATTR is not set then we keep
+ * the old behavior and return -ENODATA for
+ * getxattr() when mbo_eadatasize is 0. But
+ * -ENODATA only makes sense for getxattr()
+ * and not for listxattr(). */
+ if (body->mbo_valid & OBD_MD_FLXATTR)
+ GOTO(out, rc = 0);
+ else if (valid == OBD_MD_FLXATTR)
+ GOTO(out, rc = -ENODATA);
+ else
+ GOTO(out, rc = 0);
+ }
/* do not need swab xattr data */
xdata = req_capsule_server_sized_get(&req->rq_pill, &RMF_EADATA,
LASSERT(body);
/* only detect the xattr size */
- if (size == 0)
+ if (size == 0) {
+ /* LU-11109: Older MDTs do not distinguish
+ * between nonexistent xattrs and zero length
+ * values in this case. Newer MDTs will return
+ * -ENODATA or set OBD_MD_FLXATTR. */
GOTO(out, rc = body->mbo_eadatasize);
+ }
if (size < body->mbo_eadatasize) {
CERROR("server bug: replied size %u > %u\n",
GOTO(out, rc = -ERANGE);
}
- if (body->mbo_eadatasize == 0)
- GOTO(out, rc = -ENODATA);
+ if (body->mbo_eadatasize == 0) {
+ /* LU-11109: Newer MDTs set OBD_MD_FLXATTR on
+ * success so that we can distinguish between
+ * zero length value and nonexistent xattr.
+ *
+ * If OBD_MD_FLXATTR is not set then we keep
+ * the old behavior and return -ENODATA for
+ * getxattr() when mbo_eadatasize is 0. But
+ * -ENODATA only makes sense for getxattr()
+ * and not for listxattr(). */
+ if (body->mbo_valid & OBD_MD_FLXATTR)
+ GOTO(out, rc = 0);
+ else if (valid == OBD_MD_FLXATTR)
+ GOTO(out, rc = -ENODATA);
+ else
+ GOTO(out, rc = 0);
+ }
/* do not need swab xattr data */
xdata = req_capsule_server_sized_get(&req->rq_pill, &RMF_EADATA,
out:
if (rc >= 0) {
mdt_counter_incr(req, LPROC_MDT_GETXATTR);
+ /* LU-11109: Set OBD_MD_FLXATTR on success so that
+ * newer clients can distinguish between nonexistent
+ * xattrs and zero length values. */
+ repbody->mbo_valid |= OBD_MD_FLXATTR;
repbody->mbo_eadatasize = rc;
rc = 0;
}
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
+#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
"Usage: %s filename command-sequence [path...]\n"
" command-sequence items:\n"
" A fsetxattr(\"user.multiop\")\n"
-" a fgetxattr(\"user.multiop\")\n"
+" a[num] fgetxattr(\"user.multiop\") [optional buffer size, default 0]\n"
" c close\n"
" B[num] call setstripe ioctl to create stripes\n"
" C[num] create with optional stripes\n"
struct lu_fid fid;
struct timespec ts;
struct lov_user_md_v3 lum;
+ char *xattr_buf = NULL;
+ size_t xattr_buf_size = 0;
long long rc = 0;
long long last_rc;
}
break;
case 'a':
- rc = fgetxattr(fd, XATTR, NULL, 0);
+ len = atoi(commands + 1);
+ if (xattr_buf_size < len) {
+ xattr_buf = realloc(xattr_buf, len);
+ if (xattr_buf == NULL) {
+ save_errno = errno;
+ perror("allocating xattr buffer\n");
+ exit(save_errno);
+ }
+
+ xattr_buf_size = len;
+ }
+
+ rc = fgetxattr(fd, XATTR, xattr_buf, len);
if (rc < 0) {
save_errno = errno;
perror("fgetxattr");
}
run_test 102s "getting nonexistent xattrs should fail"
+test_102t() {
+ [ $(lustre_version_code $SINGLEMDS) -lt $(version_code 2.11.52) ] &&
+ skip "MDS needs to be at least 2.11.52"
+
+ local save="$TMP/$TESTSUITE-$TESTNAME.parameters"
+
+ save_lustre_params client "llite.*.xattr_cache" > $save
+
+ for cache in 0 1; do
+ lctl set_param llite.*.xattr_cache=$cache
+
+ for buf_size in 0 256; do
+ rm -f $DIR/$tfile
+ touch $DIR/$tfile || error "touch"
+ setfattr -n user.multiop $DIR/$tfile
+ $MULTIOP $DIR/$tfile oa$buf_size ||
+ error "cannot get zero length xattr value (buf_size = $buf_size)"
+ done
+ done
+
+ restore_lustre_params < $save
+}
+run_test 102t "zero length xattr values handled correctly"
+
run_acl_subtest()
{
$LUSTRE/tests/acl/run $LUSTRE/tests/acl/$1.test