if (rc)
GOTO(out, rc);
+ /* If encryption context was returned by MDT, put it in
+ * inode now to save an extra getxattr.
+ */
+ if (body->mbo_valid & OBD_MD_ENCCTX) {
+ void *encctx = req_capsule_server_get(&req->rq_pill,
+ &RMF_FILE_ENCCTX);
+ __u32 encctxlen = req_capsule_get_size(&req->rq_pill,
+ &RMF_FILE_ENCCTX,
+ RCL_SERVER);
+
+ if (encctxlen) {
+ CDEBUG(D_SEC,
+ "server returned encryption ctx for "DFID"\n",
+ PFID(ll_inode2fid(child)));
+ rc = ll_xattr_cache_insert(child,
+ LL_XATTR_NAME_ENCRYPTION_CONTEXT,
+ encctx, encctxlen);
+ if (rc)
+ CWARN("%s: cannot set enc ctx for "DFID": rc = %d\n",
+ ll_i2sbi(child)->ll_fsname,
+ PFID(ll_inode2fid(child)), rc);
+ }
+ }
+
CDEBUG(D_READA, "%s: setting %.*s"DFID" l_data to inode %p\n",
ll_i2sbi(dir)->ll_fsname, entry->se_qstr.len,
entry->se_qstr.name, PFID(ll_inode2fid(child)), child);
ENTRY;
if (ll_xattr_cache_find(cache, xattr_name, &xattr) == 0) {
+ if (!strcmp(xattr_name, LL_XATTR_NAME_ENCRYPTION_CONTEXT))
+ /* it means enc ctx was already in cache,
+ * ignore error as it cannot be modified
+ */
+ RETURN(0);
+
CDEBUG(D_CACHE, "duplicate xattr: [%s]\n", xattr_name);
RETURN(-EPROTO);
}
}
/**
- * Check if the xattr cache is initialized (filled).
+ * Check if the xattr cache is initialized.
*
* \retval 0 @cache is not initialized
* \retval 1 @cache is initialized
}
/**
+ * Check if the xattr cache is filled.
+ *
+ * \retval 0 @cache is not filled
+ * \retval 1 @cache is filled
+ */
+static int ll_xattr_cache_filled(struct ll_inode_info *lli)
+{
+ return test_bit(LLIF_XATTR_CACHE_FILLED, &lli->lli_flags);
+}
+
+/**
* This finalizes the xattr cache.
*
* Free all xattr memory. @lli is the inode info pointer.
while (ll_xattr_cache_del(&lli->lli_xattrs, NULL) == 0)
/* empty loop */ ;
+ clear_bit(LLIF_XATTR_CACHE_FILLED, &lli->lli_flags);
clear_bit(LLIF_XATTR_CACHE, &lli->lli_flags);
RETURN(0);
* Find or request an LDLM lock with xattr data.
* Since LDLM does not provide API for atomic match_or_enqueue,
* the function handles it with a separate enq lock.
- * If successful, the function exits with the list lock held.
+ * If successful, the function exits with a write lock held
+ * on lli_xattrs_list_rwsem.
*
* \retval 0 no error occured
* \retval -ENOMEM not enough memory
mutex_lock(&lli->lli_xattrs_enq_lock);
/* inode may have been shrunk and recreated, so data is gone, match lock
* only when data exists. */
- if (ll_xattr_cache_valid(lli)) {
+ if (ll_xattr_cache_filled(lli)) {
/* Try matching first. */
mode = ll_take_md_lock(inode, MDS_INODELOCK_XATTR, &lockh, 0,
LCK_PR);
/**
* Refill the xattr cache.
*
- * Fetch and cache the whole of xattrs for @inode, acquiring a read lock.
+ * Fetch and cache the whole of xattrs for @inode, thanks to the write lock
+ * on lli_xattrs_list_rwsem obtained from ll_xattr_find_get_lock().
+ * If successful, this write lock is kept.
*
* \retval 0 no error occured
* \retval -EPROTO network protocol error
GOTO(err_req, rc);
/* Do we have the data at this point? */
- if (ll_xattr_cache_valid(lli)) {
+ if (ll_xattr_cache_filled(lli)) {
ll_stats_ops_tally(sbi, LPROC_LL_GETXATTR_HITS, 1);
ll_intent_drop_lock(&oit);
GOTO(err_req, rc = 0);
CDEBUG(D_CACHE, "caching: xdata=%p xtail=%p\n", xdata, xtail);
- ll_xattr_cache_init(lli);
+ if (!ll_xattr_cache_valid(lli))
+ ll_xattr_cache_init(lli);
for (i = 0; i < body->mbo_max_mdsize; i++) {
CDEBUG(D_CACHE, "caching [%s]=%.*s\n", xdata, *xsizes, xval);
if (xdata != xtail || xval != xvtail)
CERROR("a hole in xattr data\n");
+ else
+ set_bit(LLIF_XATTR_CACHE_FILLED, &lli->lli_flags);
ll_set_lock_data(sbi->ll_md_exp, inode, &oit, NULL);
ll_intent_drop_lock(&oit);
LASSERT(!!(valid & OBD_MD_FLXATTR) ^ !!(valid & OBD_MD_FLXATTRLS));
down_read(&lli->lli_xattrs_list_rwsem);
- if (!ll_xattr_cache_valid(lli)) {
+ /* For performance reasons, we do not want to refill complete xattr
+ * cache if we are just interested in encryption context.
+ */
+ if ((valid & OBD_MD_FLXATTRLS ||
+ strcmp(name, LL_XATTR_NAME_ENCRYPTION_CONTEXT) != 0) &&
+ !ll_xattr_cache_filled(lli)) {
up_read(&lli->lli_xattrs_list_rwsem);
rc = ll_xattr_cache_refill(inode);
if (rc)
RETURN(rc);
+ /* Turn the write lock obtained in ll_xattr_cache_refill()
+ * into a read lock.
+ */
downgrade_write(&lli->lli_xattrs_list_rwsem);
} else {
ll_stats_ops_tally(ll_i2sbi(inode), LPROC_LL_GETXATTR_HITS, 1);
}
+ if (!ll_xattr_cache_valid(lli))
+ GOTO(out, rc = -ENODATA);
+
if (valid & OBD_MD_FLXATTR) {
struct ll_xattr_entry *xattr;
ENTRY;
- down_read(&lli->lli_xattrs_list_rwsem);
+ down_write(&lli->lli_xattrs_list_rwsem);
if (!ll_xattr_cache_valid(lli))
ll_xattr_cache_init(lli);
- rc = ll_xattr_cache_add(&lli->lli_xattrs, name, buffer,
- size);
- up_read(&lli->lli_xattrs_list_rwsem);
-
- if (rc == -EPROTO &&
- strcmp(name, LL_XATTR_NAME_ENCRYPTION_CONTEXT) == 0)
- /* it means enc ctx was already in cache,
- * ignore error as it cannot be modified
- */
- rc = 0;
+ rc = ll_xattr_cache_add(&lli->lli_xattrs, name, buffer, size);
+ up_write(&lli->lli_xattrs_list_rwsem);
RETURN(rc);
}
}
cleanup_for_enc_tests() {
+ rm -rf $DIR/$tdir
+
# remount client normally
if is_mounted $MOUNT; then
umount_client $MOUNT || error "umount $MOUNT failed"
trace_cmd() {
local cmd="$@"
- local xattr_name="security.c"
cancel_lru_locks
$LCTL set_param debug=+info
eval $cmd
[ $? -eq 0 ] || error "$cmd failed"
- $LCTL dk | grep -E "get xattr '${xattr_name}'|get xattrs"
+ if [ -z "$MATCHING_STRING" ]; then
+ $LCTL dk | grep -E "get xattr 'security.c'|get xattrs"
+ else
+ $LCTL dk | grep -E "$MATCHING_STRING"
+ fi
[ $? -ne 0 ] || error "get xattr event was triggered"
}
trace_cmd stat $dirname/f1
trace_cmd cat $dirname/f1
dd if=/dev/zero of=$dirname/f1 bs=1M count=10 conv=fsync
- trace_cmd $TRUNCATE $dirname/f1 10240
+ MATCHING_STRING="get xattr 'security.c'" \
+ trace_cmd $TRUNCATE $dirname/f1 10240
trace_cmd $LFS setstripe -E -1 -S 4M $dirname/f2
trace_cmd $LFS migrate -E -1 -S 256K $dirname/f2
trace_cmd stat $dirname/f1
trace_cmd cat $dirname/f1
dd if=/dev/zero of=$dirname/f1 bs=1M count=10 conv=fsync
- trace_cmd $TRUNCATE $dirname/f1 10240
+ MATCHING_STRING="get xattr 'security.c'" \
+ trace_cmd $TRUNCATE $dirname/f1 10240
trace_cmd $LFS setstripe -E -1 -S 4M $dirname/f2
trace_cmd $LFS migrate -E -1 -S 256K $dirname/f2
else
}
run_test 57 "security.c xattr protection"
+test_58() {
+ local testdir=$DIR/$tdir/mytestdir
+ local testfile=$DIR/$tdir/$tfile
+
+ [[ $(facet_fstype ost1) == zfs ]] && skip "skip ZFS backend"
+
+ $LCTL get_param mdc.*.import | grep -q client_encryption ||
+ skip "client encryption not supported"
+
+ mount.lustre --help |& grep -q "test_dummy_encryption:" ||
+ skip "need dummy encryption support"
+
+ stack_trap cleanup_for_enc_tests EXIT
+ setup_for_enc_tests
+
+ touch $DIR/$tdir/$tfile
+ mkdir $DIR/$tdir/subdir
+
+ cancel_lru_locks
+ sync ; sync
+ echo 3 > /proc/sys/vm/drop_caches
+
+ ll_decode_linkea $DIR/$tdir/$tfile || error "cannot read $tfile linkea"
+ ll_decode_linkea $DIR/$tdir/subdir || error "cannot read subdir linkea"
+
+ for ((i = 0; i < 1000; i = $((i+1)))); do
+ mkdir -p $DIR/$tdir/d${i}
+ touch $DIR/$tdir/f${i}
+ createmany -m $DIR/$tdir/d${i}/f 5 > /dev/null
+ done
+
+ cancel_lru_locks
+ sync ; sync
+ echo 3 > /proc/sys/vm/drop_caches
+
+ sleep 10
+ ls -ailR $MOUNT > /dev/null || error "fail to ls"
+}
+run_test 58 "access to enc file's xattrs"
+
log "cleanup: ======================================================"
sec_unsetup() {