struct lu_ucred;
+extern int lustre_groups_search(struct group_info *group_info, gid_t grp);
extern void lustre_groups_from_list(struct group_info *ginfo, gid_t *glist);
+extern void lustre_list_from_groups(gid_t *glist, struct group_info *ginfo);
extern void lustre_groups_sort(struct group_info *group_info);
extern int lustre_in_group_p(struct lu_ucred *mu, gid_t grp);
OBD_FAIL_TIMEOUT(OBD_FAIL_LLITE_OPEN_DELAY, cfs_fail_val);
- rc = md_intent_lock(sbi->ll_md_exp, op_data, itp, &req,
- &ll_md_blocking_ast, 0);
+ rc = ll_intent_lock(sbi->ll_md_exp, op_data, itp, &req,
+ &ll_md_blocking_ast, 0, true);
kfree(name);
ll_finish_md_op_data(op_data);
if (rc == -ESTALE) {
it.it_flags = fmode | open_flags;
it.it_flags |= MDS_OPEN_LOCK | MDS_OPEN_BY_FID | MDS_OPEN_LEASE;
- rc = md_intent_lock(sbi->ll_md_exp, op_data, &it, &req,
+ rc = ll_intent_lock(sbi->ll_md_exp, op_data, &it, &req,
&ll_md_blocking_lease_ast,
/* LDLM_FL_NO_LRU: To not put the lease lock into LRU list, otherwise
* it can be cancelled which may mislead applications that the lease is
* LDLM_FL_EXCL: Set this flag so that it won't be matched by normal
* open in ll_md_blocking_ast(). Otherwise as ll_md_blocking_lease_ast
* doesn't deal with openhandle, so normal openhandle will be leaked. */
- LDLM_FL_NO_LRU | LDLM_FL_EXCL);
+ LDLM_FL_NO_LRU | LDLM_FL_EXCL,
+ true);
ll_finish_md_op_data(op_data);
ptlrpc_req_finished(req);
if (rc < 0)
/* Call getattr by fid */
if (exp_connect_flags2(exp) & OBD_CONNECT2_GETATTR_PFID)
op_data->op_flags = MF_GETATTR_BY_FID;
- rc = md_intent_lock(exp, op_data, &oit, &req, &ll_md_blocking_ast, 0);
+ rc = ll_intent_lock(exp, op_data, &oit, &req,
+ &ll_md_blocking_ast, 0, true);
ll_finish_md_op_data(op_data);
if (rc < 0) {
rc = ll_inode_revalidate_fini(inode, rc);
LDLM_DEBUG_NOLOCK("%s: requeue layout lock for file "DFID"(%p)",
sbi->ll_fsname, PFID(&lli->lli_fid), inode);
- rc = md_intent_lock(sbi->ll_md_exp, op_data, &it, &req,
- &ll_md_blocking_ast, 0);
+ rc = ll_intent_lock(sbi->ll_md_exp, op_data, &it, &req,
+ &ll_md_blocking_ast, 0, true);
if (it.it_request != NULL)
ptlrpc_req_finished(it.it_request);
it.it_request = NULL;
struct dentry *ll_splice_alias(struct inode *inode, struct dentry *de);
int ll_rmdir_entry(struct inode *dir, char *name, int namelen);
void ll_update_times(struct ptlrpc_request *request, struct inode *inode);
+int ll_intent_lock(struct obd_export *exp, struct md_op_data *op_data,
+ struct lookup_intent *it, struct ptlrpc_request **reqp,
+ ldlm_blocking_callback cb_blocking, __u64 extra_lock_flags,
+ bool tryagain);
/* llite/rw.c */
int ll_writepage(struct page *page, struct writeback_control *wbc);
return rc;
}
+static int get_acl_from_req(struct ptlrpc_request *req, struct posix_acl **acl)
+{
+ struct mdt_body *body;
+ void *buf;
+ int rc;
+
+ body = req_capsule_server_get(&req->rq_pill, &RMF_MDT_BODY);
+ if (!body->mbo_aclsize) {
+ *acl = NULL;
+ return 0;
+ }
+
+ buf = req_capsule_server_sized_get(&req->rq_pill, &RMF_ACL,
+ body->mbo_aclsize);
+ if (!buf)
+ return -EPROTO;
+
+ *acl = posix_acl_from_xattr(&init_user_ns, buf, body->mbo_aclsize);
+ if (IS_ERR_OR_NULL(*acl)) {
+ rc = *acl ? PTR_ERR(*acl) : 0;
+ CDEBUG(D_SEC, "convert xattr to acl: %d\n", rc);
+ return rc;
+ }
+
+ rc = posix_acl_valid(&init_user_ns, *acl);
+ if (rc) {
+ CDEBUG(D_SEC, "validate acl: %d\n", rc);
+ posix_acl_release(*acl);
+ return rc;
+ }
+
+ return 0;
+}
+
+static inline int accmode_from_openflags(u64 open_flags)
+{
+ unsigned int may_mask = 0;
+
+ if (open_flags & (FMODE_READ | FMODE_PREAD))
+ may_mask |= MAY_READ;
+ if (open_flags & (FMODE_WRITE | FMODE_PWRITE))
+ may_mask |= MAY_WRITE;
+ if (open_flags & FMODE_EXEC)
+ may_mask = MAY_EXEC;
+
+ return may_mask;
+}
+
+static __u32 get_uc_group_from_acl(const struct posix_acl *acl, int want)
+{
+ const struct posix_acl_entry *pa, *pe;
+
+ FOREACH_ACL_ENTRY(pa, acl, pe) {
+ switch (pa->e_tag) {
+ case ACL_GROUP_OBJ:
+ case ACL_GROUP:
+ if (in_group_p(pa->e_gid) &&
+ (pa->e_perm & want) == want)
+ return (__u32)from_kgid(&init_user_ns,
+ pa->e_gid);
+ break;
+ default:
+ /* nothing to do */
+ break;
+ }
+ }
+
+ return (__u32)__kgid_val(INVALID_GID);
+}
+
+/* This function implements a retry mechanism on top of md_intent_lock().
+ * This is useful because the client can provide at most 2 supplementary
+ * groups in the request sent to the MDS, but sometimes it does not know
+ * which ones are useful for credentials calculation on server side. For
+ * instance in case of lookup, the client does not have the child inode yet
+ * when it sends the intent lock request.
+ * Hopefully, the server can hint at the useful groups, by putting in the
+ * request reply the target inode's GID, and also its ACL.
+ * So in case the server replies -EACCES, we check the user's credentials
+ * against those, and try again the intent lock request if we find a matching
+ * supplementary group.
+ */
+int ll_intent_lock(struct obd_export *exp, struct md_op_data *op_data,
+ struct lookup_intent *it, struct ptlrpc_request **reqp,
+ ldlm_blocking_callback cb_blocking, __u64 extra_lock_flags,
+ bool tryagain)
+{
+ int rc;
+
+ ENTRY;
+
+ rc = md_intent_lock(exp, op_data, it, reqp, cb_blocking,
+ extra_lock_flags);
+ if (rc == -EACCES && tryagain && it->it_op & IT_OPEN &&
+ it_disposition(it, DISP_OPEN_DENY) && *reqp) {
+ struct mdt_body *body;
+ __u32 new_suppgid;
+
+ body = req_capsule_server_get(&(*reqp)->rq_pill, &RMF_MDT_BODY);
+ new_suppgid = body->mbo_gid;
+ if (op_data->op_suppgids[0] == body->mbo_gid ||
+ op_data->op_suppgids[1] == body->mbo_gid ||
+ !in_group_p(make_kgid(&init_user_ns, body->mbo_gid))) {
+ int accmode = accmode_from_openflags(it->it_flags);
+ struct posix_acl *acl;
+
+ rc = get_acl_from_req(*reqp, &acl);
+ if (rc || !acl)
+ GOTO(out, rc = -EACCES);
+
+ new_suppgid = get_uc_group_from_acl(acl, accmode);
+ posix_acl_release(acl);
+
+ if (new_suppgid == (__u32)__kgid_val(INVALID_GID))
+ GOTO(out, rc = -EACCES);
+ }
+
+ if (!(it->it_flags & MDS_OPEN_BY_FID))
+ fid_zero(&op_data->op_fid2);
+ op_data->op_suppgids[1] = new_suppgid;
+ ptlrpc_req_finished(*reqp);
+ *reqp = NULL;
+ ll_intent_release(it);
+ rc = md_intent_lock(exp, op_data, it, reqp, cb_blocking,
+ extra_lock_flags);
+ }
+
+out:
+ RETURN(rc);
+}
+
static struct dentry *ll_lookup_it(struct inode *parent, struct dentry *dentry,
struct lookup_intent *it,
void **secctx, __u32 *secctxlen,
it->it_flags |= MDS_OPEN_PCC;
}
- rc = md_intent_lock(ll_i2mdexp(parent), op_data, it, &req,
- &ll_md_blocking_ast, 0);
/* If the MDS allows the client to chgrp (CFS_SETGRP_PERM), but the
* client does not know which suppgid should be sent to the MDS, or
* some other(s) changed the target file's GID after this RPC sent
* to the MDS with the suppgid as the original GID, then we should
* try again with right suppgid. */
- if (rc == -EACCES && it->it_op & IT_OPEN &&
- it_disposition(it, DISP_OPEN_DENY)) {
- struct mdt_body *body;
-
- LASSERT(req != NULL);
-
- body = req_capsule_server_get(&req->rq_pill, &RMF_MDT_BODY);
- if (op_data->op_suppgids[0] == body->mbo_gid ||
- op_data->op_suppgids[1] == body->mbo_gid ||
- !in_group_p(make_kgid(&init_user_ns, body->mbo_gid)))
- GOTO(out, retval = ERR_PTR(-EACCES));
-
- fid_zero(&op_data->op_fid2);
- op_data->op_suppgids[1] = body->mbo_gid;
- ptlrpc_req_finished(req);
- req = NULL;
- ll_intent_release(it);
- rc = md_intent_lock(ll_i2mdexp(parent), op_data, it, &req,
- &ll_md_blocking_ast, 0);
- }
-
+ rc = ll_intent_lock(ll_i2mdexp(parent), op_data, it, &req,
+ &ll_md_blocking_ast, 0, true);
if (rc < 0)
GOTO(out, retval = ERR_PTR(rc));
op_data->op_valid = OBD_MD_FLXATTR | OBD_MD_FLXATTRLS;
- rc = md_intent_lock(exp, op_data, oit, req, &ll_md_blocking_ast, 0);
+ rc = ll_intent_lock(exp, op_data, oit, req,
+ &ll_md_blocking_ast, 0, true);
ll_finish_md_op_data(op_data);
*req = oit->it_request;
RETURN(rc);
}
-struct md_identity *mdt_identity_get(struct upcall_cache *cache, __u32 uid)
+struct md_identity *mdt_identity_get(struct upcall_cache *cache, __u32 uid,
+ struct mdt_thread_info *info)
{
struct upcall_cache_entry *entry;
if (!cache)
return ERR_PTR(-ENOENT);
- entry = upcall_cache_get_entry(cache, (__u64)uid, NULL);
+ entry = upcall_cache_get_entry(cache, (__u64)uid,
+ info ? mdt_ucred(info) : NULL);
if (unlikely(!entry))
return ERR_PTR(-ENOENT);
if (IS_ERR(entry))
extern struct upcall_cache_ops mdt_identity_upcall_cache_ops;
-struct md_identity *mdt_identity_get(struct upcall_cache *, __u32);
+struct md_identity *mdt_identity_get(struct upcall_cache *cache, __u32 uid,
+ struct mdt_thread_info *info);
void mdt_identity_put(struct upcall_cache *, struct md_identity *);
struct md_identity *identity;
identity = mdt_identity_get(mdt->mdt_identity_cache,
- pud->pud_uid);
+ pud->pud_uid, info);
if (IS_ERR(identity)) {
if (unlikely(PTR_ERR(identity) == -EREMCHG)) {
ucred->uc_identity = NULL;
if (is_identity_get_disabled(mdt->mdt_identity_cache))
RETURN(0);
- identity = mdt_identity_get(mdt->mdt_identity_cache, pud->pud_uid);
+ identity = mdt_identity_get(mdt->mdt_identity_cache, pud->pud_uid,
+ info);
if (IS_ERR(identity)) {
if (unlikely(PTR_ERR(identity) == -EREMCHG)) {
RETURN(0);
if (!is_identity_get_disabled(mdt->mdt_identity_cache)) {
identity = mdt_identity_get(mdt->mdt_identity_cache,
- uc->uc_fsuid);
+ uc->uc_fsuid, info);
if (IS_ERR(identity)) {
if (unlikely(PTR_ERR(identity) == -EREMCHG ||
uc->uc_cap & CFS_CAP_FS_MASK)) {
* groups_search() is copied from linux kernel!
* A simple bsearch.
*/
-static int lustre_groups_search(struct group_info *group_info,
- gid_t grp)
+int lustre_groups_search(struct group_info *group_info, gid_t grp)
{
int left, right;
}
return 0;
}
+EXPORT_SYMBOL(lustre_groups_search);
void lustre_groups_from_list(struct group_info *ginfo, gid_t *glist)
{
}
EXPORT_SYMBOL(lustre_groups_from_list);
+void lustre_list_from_groups(gid_t *glist, struct group_info *ginfo)
+{
+#ifdef HAVE_GROUP_INFO_GID
+ memcpy(glist, ginfo->gid, ginfo->ngroups * sizeof(__u32));
+#else
+ int i;
+ int count = ginfo->ngroups;
+
+ /* fill in gid array from group_info */
+ for (i = 0; i < ginfo->nblocks && count > 0; i++) {
+ int cp_count = min(CFS_NGROUPS_PER_BLOCK, count);
+ int off = i * CFS_NGROUPS_PER_BLOCK;
+ int len = cp_count * sizeof(*glist);
+
+ memcpy(glist + off, ginfo->blocks[i], len);
+ count -= cp_count;
+ }
+#endif
+}
+EXPORT_SYMBOL(lustre_list_from_groups);
+
/* groups_sort() is copied from linux kernel! */
/* a simple shell-metzner sort */
void lustre_groups_sort(struct group_info *group_info)
#include <libcfs/libcfs.h>
#include <uapi/linux/lnet/lnet-types.h>
+#include <lustre_idmap.h>
+#include <md_object.h>
#include <upcall_cache.h>
+/* The special identity_upcall value "INTERNAL" implements a particular behavior
+ * which does not involve an actual upcall. Instead, the cache is filled with
+ * supplementary groups read from the user's credentials provided as input
+ * (usually got from the client request), cumulatively at each request.
+ */
+#define IDENTITY_UPCALL_INTERNAL "INTERNAL"
+
static struct upcall_cache_entry *alloc_entry(struct upcall_cache *cache,
__u64 key, void *args)
{
}
static inline int refresh_entry(struct upcall_cache *cache,
- struct upcall_cache_entry *entry)
+ struct upcall_cache_entry *entry,
+ __u32 fsgid, int ngroups, gid_t *grouplist)
{
+ if (strcmp(cache->uc_upcall, IDENTITY_UPCALL_INTERNAL) == 0) {
+ struct group_info *ginfo = NULL;
+
+ if (ngroups && grouplist) {
+ ginfo = groups_alloc(ngroups);
+ if (!ginfo) {
+ CDEBUG(D_OTHER,
+ "failed to alloc %d groups: rc = %d\n",
+ ngroups, -ENOMEM);
+ return -ENOMEM;
+ }
+ lustre_groups_from_list(ginfo, grouplist);
+ lustre_groups_sort(ginfo);
+ }
+
+ spin_lock(&cache->uc_lock);
+ get_entry(entry);
+ entry->u.identity.mi_uid = entry->ue_key;
+ entry->u.identity.mi_gid = fsgid;
+ entry->u.identity.mi_ginfo = ginfo;
+ entry->u.identity.mi_nperms = 0;
+ entry->u.identity.mi_perms = NULL;
+ entry->ue_expire = ktime_get_seconds() + cache->uc_entry_expire;
+ UC_CACHE_SET_VALID(entry);
+ UC_CACHE_CLEAR_ACQUIRING(entry);
+ spin_unlock(&cache->uc_lock);
+ put_entry(cache, entry);
+
+ return 0;
+ }
+
LASSERT(cache->uc_ops->do_upcall);
return cache->uc_ops->do_upcall(cache, entry);
}
__u64 key, void *args)
{
struct upcall_cache_entry *entry = NULL, *new = NULL, *next;
+ struct lu_ucred *uc = (struct lu_ucred *)args;
+ gid_t *grouplist = NULL, *glist_p;
struct list_head *head;
wait_queue_entry_t wait;
- int rc, found;
+ int rc, found, i, ngroups = 0;
ENTRY;
LASSERT(cache);
}
get_entry(entry);
+ spin_unlock(&cache->uc_lock);
+ if (!grouplist && !strcmp(cache->uc_upcall, IDENTITY_UPCALL_INTERNAL) &&
+ uc && uc->uc_suppgids[0] != -1) {
+ struct md_identity *identity = &entry->u.identity;
+ bool supp_in_ginfo[2] = { false, uc->uc_suppgids[1] == -1 };
+
+ if (!identity->mi_ginfo ||
+ !identity->mi_ginfo->ngroups)
+ ngroups = 0;
+ else
+ ngroups = identity->mi_ginfo->ngroups;
+
+ /* check if provided supp groups are already in cache */
+ for (i = 0; i < 2 && uc->uc_suppgids[i] != -1; i++) {
+ if (unlikely(uc->uc_suppgids[i] == uc->uc_fsuid)) {
+ /* Do not place user's group ID in group list */
+ supp_in_ginfo[i] = true;
+ } else if (ngroups) {
+ atomic_inc(&identity->mi_ginfo->usage);
+ supp_in_ginfo[i] =
+ lustre_groups_search(identity->mi_ginfo,
+ uc->uc_suppgids[i]);
+ atomic_dec(&identity->mi_ginfo->usage);
+ }
+ }
+
+ /* build new list of groups, which is a merge of provided supp
+ * groups and all other groups already in cache
+ */
+ if (!supp_in_ginfo[0] || !supp_in_ginfo[1]) {
+ ngroups += !supp_in_ginfo[0] + !supp_in_ginfo[1];
+ CFS_ALLOC_PTR_ARRAY(grouplist, ngroups);
+ if (grouplist == NULL)
+ GOTO(out, entry = ERR_PTR(-ENOMEM));
+
+ glist_p = grouplist;
+ for (i = 0; i < 2; i++) {
+ if (!supp_in_ginfo[i])
+ *(glist_p++) = uc->uc_suppgids[i];
+ }
+
+ if (identity->mi_ginfo && identity->mi_ginfo->ngroups) {
+ atomic_inc(&identity->mi_ginfo->usage);
+ lustre_list_from_groups(glist_p,
+ identity->mi_ginfo);
+ atomic_dec(&identity->mi_ginfo->usage);
+ }
+
+ if (!UC_CACHE_IS_NEW(entry)) {
+ /* force refresh as an existing cache entry
+ * cannot be modified
+ */
+ spin_lock(&cache->uc_lock);
+ entry->ue_expire = ktime_get_seconds();
+ spin_unlock(&cache->uc_lock);
+ }
+ }
+ }
+
+ spin_lock(&cache->uc_lock);
/* acquire for new one */
if (UC_CACHE_IS_NEW(entry)) {
+ gid_t fsgid = (__u32)__kgid_val(INVALID_GID);
+
+ if (uc)
+ fsgid = uc->uc_fsgid;
+
UC_CACHE_SET_ACQUIRING(entry);
UC_CACHE_CLEAR_NEW(entry);
spin_unlock(&cache->uc_lock);
- rc = refresh_entry(cache, entry);
+ rc = refresh_entry(cache, entry, fsgid, ngroups, grouplist);
spin_lock(&cache->uc_lock);
entry->ue_acquire_expire = ktime_get_seconds() +
cache->uc_acquire_expire;
/* Now we know it's good */
out:
spin_unlock(&cache->uc_lock);
+ if (grouplist)
+ CFS_FREE_PTR_ARRAY(grouplist, ngroups);
RETURN(entry);
}
EXPORT_SYMBOL(upcall_cache_get_entry);
#endif
static const char usage[] =
-"Usage: %s -u user_id [-g grp_id] [-v euid] [-j egid] [-G[gid0,gid1,...]] command\n"
+"Usage: %s -u user_id [-g grp_id] [-v euid] [-j egid] [-G<gid0,gid1,...>] command\n"
" -u user_id switch to UID user_id\n"
" -g grp_id switch to GID grp_id\n"
" -v euid switch euid to UID\n"
" -j egid switch egid to GID\n"
-" -G[gid0,gid1,...] set supplementary groups\n";
+" -G<gid0,gid1,...> set supplementary groups\n";
void Usage_and_abort(const char *name)
{
break;
case 'G':
- num_supp = 0;
- if (!optarg || !isdigit(optarg[0]))
+ if (!optarg || !isdigit(optarg[0])) {
+ fprintf(stderr,
+ "Provided parameter '%s' for option '-G' is bad\n",
+ optarg);
+ Usage_and_abort(name);
break;
+ }
+ num_supp = 0;
while ((grp = strsep(&optarg, ",")) != NULL) {
printf("adding supp group %d\n", atoi(grp));
supp_groups[num_supp++] = atoi(grp);
exit(-1);
}
- if (num_supp >= 0) {
- status = setgroups(num_supp, supp_groups);
- if (status == -1) {
- perror("setting supplementary groups");
- exit(-1);
- }
+ if (num_supp == -1) {
+ /* at least one supp group needs to be provided,
+ * so take the gid
+ */
+ num_supp = 1;
+ supp_groups[0] = grp_id;
+ }
+ status = setgroups(num_supp, supp_groups);
+ if (status == -1) {
+ perror("setting supplementary groups");
+ exit(-1);
}
/* set UID */
local hsm_root="$mntpt/$tdir"
local file=$DIR/$tfile
local fsuuid=$($LFS getname $MOUNT | awk '{print $1}')
+ local runascmd="$RUNAS -G0"
setup_loopdev client $loopfile $mntpt 60
mkdir $hsm_root || error "mkdir $hsm_root failed"
$LCTL pcc list $MOUNT
local mode=$($LCTL get_param -n llite.$fsuuid.pcc_mode)
- $RUNAS id
+ $runascmd id
echo "Mode: $mode"
echo "QQQQQ" > $file || error "write $file failed"
$LCTL set_param llite.$fsuuid.pcc_mode="0" ||
error "Set PCC mode failed"
stack_trap "$LCTL set_param llite.$fsuuid.pcc_mode=$mode" EXIT
- $RUNAS $LFS pcc attach -r $file &&
+ $runascmd $LFS pcc attach -r $file &&
error "User should not attach $file"
- $RUNAS cat $file || error "cat $file failed"
+ $runascmd cat $file || error "cat $file failed"
check_lpcc_state $file "none" client
$LCTL set_param llite.$fsuuid.pcc_mode="0400" ||
error "Set PCC mode failed"
stack_trap "$LCTL set_param llite.$fsuuid.pcc_mode=$mode" EXIT
- $RUNAS $LFS pcc attach -r $file &&
+ $runascmd $LFS pcc attach -r $file &&
error "User should not attach $file"
- $RUNAS cat $file || error "cat $file failed"
+ $runascmd cat $file || error "cat $file failed"
check_lpcc_state $file "none" client
$LCTL set_param llite.$fsuuid.pcc_mode="0004" ||
error "Set PCC mode failed"
- $RUNAS cat $file || error "cat $file failed"
+ $runascmd cat $file || error "cat $file failed"
$LFS pcc state $file
check_lpcc_state $file "readonly" client
- $RUNAS $LFS pcc detach $file || error "Detach $file failed"
+ $runascmd $LFS pcc detach $file || error "Detach $file failed"
- $RUNAS stat $file || error "stat $file failed"
+ $runascmd stat $file || error "stat $file failed"
$LFS pcc attach -r $file || error "failed to attach $file"
check_lpcc_state $file "readonly" client
- $RUNAS $LFS pcc detach $file || error "failed to detach $file"
+ $runascmd $LFS pcc detach $file || error "failed to detach $file"
$LCTL set_param llite.$fsuuid.pcc_mode="0040" ||
error "Set PCC mode failed"
chmod 660 $file || error "chmod $file failed"
- $RUNAS cat $file || error "cat $file failed"
+ $runascmd cat $file || error "cat $file failed"
$LFS pcc state $file
check_lpcc_state $file "readonly" client
- $RUNAS $LFS pcc detach $file || error "failed to detach $file"
+ $runascmd $LFS pcc detach $file || error "failed to detach $file"
- $RUNAS $LFS pcc attach -r $file || error "attach $file failed"
+ $runascmd $LFS pcc attach -r $file || error "attach $file failed"
stat $file || error "stat $file failed"
$LFS pcc state $file
check_lpcc_state $file "readonly" client
- $RUNAS $LFS pcc detach $file || error "Detach $file failed"
+ $runascmd $LFS pcc detach $file || error "Detach $file failed"
}
run_test 46 "Verify PCC mode setting works correctly"
export PERM_CMD=${PERM_CMD:-"$LCTL conf_param"}
export L_GETIDENTITY=${L_GETIDENTITY:-"$LUSTRE/utils/l_getidentity"}
- if [ ! -f "$L_GETIDENTITY" ]; then
+ if [ "$L_GETIDENTITY" != "INTERNAL" ] && [ ! -f "$L_GETIDENTITY" ]; then
if `which l_getidentity > /dev/null 2>&1`; then
export L_GETIDENTITY=$(which l_getidentity)
else