| /* |
| * GPL HEADER START |
| * |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 only, |
| * as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License version 2 for more details (a copy is included |
| * in the LICENSE file that accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License |
| * version 2 along with this program; If not, see |
| * http://www.gnu.org/licenses/gpl-2.0.html |
| * |
| * GPL HEADER END |
| */ |
| /* |
| * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. |
| * Use is subject to license terms. |
| * |
| * Copyright (c) 2012, Intel Corporation. |
| */ |
| /* |
| * This file is part of Lustre, http://www.lustre.org/ |
| * Lustre is a trademark of Sun Microsystems, Inc. |
| * |
| * lustre/llite/llite_rmtacl.c |
| * |
| * Lustre Remote User Access Control List. |
| * |
| * Author: Fan Yong <fanyong@clusterfs.com> |
| */ |
| |
| #define DEBUG_SUBSYSTEM S_LLITE |
| |
| #ifdef CONFIG_FS_POSIX_ACL |
| |
| #include "../include/lustre_lite.h" |
| #include "../include/lustre_eacl.h" |
| #include "llite_internal.h" |
| |
| static inline __u32 rce_hashfunc(uid_t id) |
| { |
| return id & (RCE_HASHES - 1); |
| } |
| |
| static inline __u32 ee_hashfunc(uid_t id) |
| { |
| return id & (EE_HASHES - 1); |
| } |
| |
| u64 rce_ops2valid(int ops) |
| { |
| switch (ops) { |
| case RMT_LSETFACL: |
| return OBD_MD_FLRMTLSETFACL; |
| case RMT_LGETFACL: |
| return OBD_MD_FLRMTLGETFACL; |
| case RMT_RSETFACL: |
| return OBD_MD_FLRMTRSETFACL; |
| case RMT_RGETFACL: |
| return OBD_MD_FLRMTRGETFACL; |
| default: |
| return 0; |
| } |
| } |
| |
| static struct rmtacl_ctl_entry *rce_alloc(pid_t key, int ops) |
| { |
| struct rmtacl_ctl_entry *rce; |
| |
| rce = kzalloc(sizeof(*rce), GFP_NOFS); |
| if (!rce) |
| return NULL; |
| |
| INIT_LIST_HEAD(&rce->rce_list); |
| rce->rce_key = key; |
| rce->rce_ops = ops; |
| |
| return rce; |
| } |
| |
| static void rce_free(struct rmtacl_ctl_entry *rce) |
| { |
| if (!list_empty(&rce->rce_list)) |
| list_del(&rce->rce_list); |
| |
| kfree(rce); |
| } |
| |
| static struct rmtacl_ctl_entry *__rct_search(struct rmtacl_ctl_table *rct, |
| pid_t key) |
| { |
| struct rmtacl_ctl_entry *rce; |
| struct list_head *head = &rct->rct_entries[rce_hashfunc(key)]; |
| |
| list_for_each_entry(rce, head, rce_list) |
| if (rce->rce_key == key) |
| return rce; |
| |
| return NULL; |
| } |
| |
| struct rmtacl_ctl_entry *rct_search(struct rmtacl_ctl_table *rct, pid_t key) |
| { |
| struct rmtacl_ctl_entry *rce; |
| |
| spin_lock(&rct->rct_lock); |
| rce = __rct_search(rct, key); |
| spin_unlock(&rct->rct_lock); |
| return rce; |
| } |
| |
| int rct_add(struct rmtacl_ctl_table *rct, pid_t key, int ops) |
| { |
| struct rmtacl_ctl_entry *rce, *e; |
| |
| rce = rce_alloc(key, ops); |
| if (!rce) |
| return -ENOMEM; |
| |
| spin_lock(&rct->rct_lock); |
| e = __rct_search(rct, key); |
| if (unlikely(e)) { |
| CWARN("Unexpected stale rmtacl_entry found: [key: %d] [ops: %d]\n", |
| (int)key, ops); |
| rce_free(e); |
| } |
| list_add_tail(&rce->rce_list, &rct->rct_entries[rce_hashfunc(key)]); |
| spin_unlock(&rct->rct_lock); |
| |
| return 0; |
| } |
| |
| int rct_del(struct rmtacl_ctl_table *rct, pid_t key) |
| { |
| struct rmtacl_ctl_entry *rce; |
| |
| spin_lock(&rct->rct_lock); |
| rce = __rct_search(rct, key); |
| if (rce) |
| rce_free(rce); |
| spin_unlock(&rct->rct_lock); |
| |
| return rce ? 0 : -ENOENT; |
| } |
| |
| void rct_init(struct rmtacl_ctl_table *rct) |
| { |
| int i; |
| |
| spin_lock_init(&rct->rct_lock); |
| for (i = 0; i < RCE_HASHES; i++) |
| INIT_LIST_HEAD(&rct->rct_entries[i]); |
| } |
| |
| void rct_fini(struct rmtacl_ctl_table *rct) |
| { |
| struct rmtacl_ctl_entry *rce; |
| int i; |
| |
| spin_lock(&rct->rct_lock); |
| for (i = 0; i < RCE_HASHES; i++) |
| while (!list_empty(&rct->rct_entries[i])) { |
| rce = list_entry(rct->rct_entries[i].next, |
| struct rmtacl_ctl_entry, rce_list); |
| rce_free(rce); |
| } |
| spin_unlock(&rct->rct_lock); |
| } |
| |
| static struct eacl_entry *ee_alloc(pid_t key, struct lu_fid *fid, int type, |
| ext_acl_xattr_header *header) |
| { |
| struct eacl_entry *ee; |
| |
| ee = kzalloc(sizeof(*ee), GFP_NOFS); |
| if (!ee) |
| return NULL; |
| |
| INIT_LIST_HEAD(&ee->ee_list); |
| ee->ee_key = key; |
| ee->ee_fid = *fid; |
| ee->ee_type = type; |
| ee->ee_acl = header; |
| |
| return ee; |
| } |
| |
| void ee_free(struct eacl_entry *ee) |
| { |
| if (!list_empty(&ee->ee_list)) |
| list_del(&ee->ee_list); |
| |
| if (ee->ee_acl) |
| lustre_ext_acl_xattr_free(ee->ee_acl); |
| |
| kfree(ee); |
| } |
| |
| static struct eacl_entry *__et_search_del(struct eacl_table *et, pid_t key, |
| struct lu_fid *fid, int type) |
| { |
| struct eacl_entry *ee; |
| struct list_head *head = &et->et_entries[ee_hashfunc(key)]; |
| |
| LASSERT(fid); |
| list_for_each_entry(ee, head, ee_list) |
| if (ee->ee_key == key) { |
| if (lu_fid_eq(&ee->ee_fid, fid) && |
| ee->ee_type == type) { |
| list_del_init(&ee->ee_list); |
| return ee; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| struct eacl_entry *et_search_del(struct eacl_table *et, pid_t key, |
| struct lu_fid *fid, int type) |
| { |
| struct eacl_entry *ee; |
| |
| spin_lock(&et->et_lock); |
| ee = __et_search_del(et, key, fid, type); |
| spin_unlock(&et->et_lock); |
| return ee; |
| } |
| |
| void et_search_free(struct eacl_table *et, pid_t key) |
| { |
| struct eacl_entry *ee, *next; |
| struct list_head *head = &et->et_entries[ee_hashfunc(key)]; |
| |
| spin_lock(&et->et_lock); |
| list_for_each_entry_safe(ee, next, head, ee_list) |
| if (ee->ee_key == key) |
| ee_free(ee); |
| |
| spin_unlock(&et->et_lock); |
| } |
| |
| int ee_add(struct eacl_table *et, pid_t key, struct lu_fid *fid, int type, |
| ext_acl_xattr_header *header) |
| { |
| struct eacl_entry *ee, *e; |
| |
| ee = ee_alloc(key, fid, type, header); |
| if (!ee) |
| return -ENOMEM; |
| |
| spin_lock(&et->et_lock); |
| e = __et_search_del(et, key, fid, type); |
| if (unlikely(e)) { |
| CWARN("Unexpected stale eacl_entry found: [key: %d] [fid: " DFID "] [type: %d]\n", |
| (int)key, PFID(fid), type); |
| ee_free(e); |
| } |
| list_add_tail(&ee->ee_list, &et->et_entries[ee_hashfunc(key)]); |
| spin_unlock(&et->et_lock); |
| |
| return 0; |
| } |
| |
| void et_init(struct eacl_table *et) |
| { |
| int i; |
| |
| spin_lock_init(&et->et_lock); |
| for (i = 0; i < EE_HASHES; i++) |
| INIT_LIST_HEAD(&et->et_entries[i]); |
| } |
| |
| void et_fini(struct eacl_table *et) |
| { |
| struct eacl_entry *ee; |
| int i; |
| |
| spin_lock(&et->et_lock); |
| for (i = 0; i < EE_HASHES; i++) |
| while (!list_empty(&et->et_entries[i])) { |
| ee = list_entry(et->et_entries[i].next, |
| struct eacl_entry, ee_list); |
| ee_free(ee); |
| } |
| spin_unlock(&et->et_lock); |
| } |
| |
| #endif |