1 /* 2 * Copyright (C) International Business Machines Corp., 2002-2004 3 * Copyright (C) Andreas Gruenbacher, 2001 4 * Copyright (C) Linus Torvalds, 1991, 1992 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 14 * the GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 */ 20 21 #include <linux/sched.h> 22 #include <linux/fs.h> 23 #include <linux/quotaops.h> 24 #include <linux/posix_acl_xattr.h> 25 #include "jfs_incore.h" 26 #include "jfs_txnmgr.h" 27 #include "jfs_xattr.h" 28 #include "jfs_acl.h" 29 30 static struct posix_acl *jfs_get_acl(struct inode *inode, int type) 31 { 32 struct posix_acl *acl; 33 char *ea_name; 34 struct jfs_inode_info *ji = JFS_IP(inode); 35 struct posix_acl **p_acl; 36 int size; 37 char *value = NULL; 38 39 switch(type) { 40 case ACL_TYPE_ACCESS: 41 ea_name = POSIX_ACL_XATTR_ACCESS; 42 p_acl = &ji->i_acl; 43 break; 44 case ACL_TYPE_DEFAULT: 45 ea_name = POSIX_ACL_XATTR_DEFAULT; 46 p_acl = &ji->i_default_acl; 47 break; 48 default: 49 return ERR_PTR(-EINVAL); 50 } 51 52 if (*p_acl != JFS_ACL_NOT_CACHED) 53 return posix_acl_dup(*p_acl); 54 55 size = __jfs_getxattr(inode, ea_name, NULL, 0); 56 57 if (size > 0) { 58 value = kmalloc(size, GFP_KERNEL); 59 if (!value) 60 return ERR_PTR(-ENOMEM); 61 size = __jfs_getxattr(inode, ea_name, value, size); 62 } 63 64 if (size < 0) { 65 if (size == -ENODATA) { 66 *p_acl = NULL; 67 acl = NULL; 68 } else 69 acl = ERR_PTR(size); 70 } else { 71 acl = posix_acl_from_xattr(value, size); 72 if (!IS_ERR(acl)) 73 *p_acl = posix_acl_dup(acl); 74 } 75 kfree(value); 76 return acl; 77 } 78 79 static int jfs_set_acl(tid_t tid, struct inode *inode, int type, 80 struct posix_acl *acl) 81 { 82 char *ea_name; 83 struct jfs_inode_info *ji = JFS_IP(inode); 84 struct posix_acl **p_acl; 85 int rc; 86 int size = 0; 87 char *value = NULL; 88 89 if (S_ISLNK(inode->i_mode)) 90 return -EOPNOTSUPP; 91 92 switch(type) { 93 case ACL_TYPE_ACCESS: 94 ea_name = POSIX_ACL_XATTR_ACCESS; 95 p_acl = &ji->i_acl; 96 break; 97 case ACL_TYPE_DEFAULT: 98 ea_name = POSIX_ACL_XATTR_DEFAULT; 99 p_acl = &ji->i_default_acl; 100 if (!S_ISDIR(inode->i_mode)) 101 return acl ? -EACCES : 0; 102 break; 103 default: 104 return -EINVAL; 105 } 106 if (acl) { 107 size = posix_acl_xattr_size(acl->a_count); 108 value = kmalloc(size, GFP_KERNEL); 109 if (!value) 110 return -ENOMEM; 111 rc = posix_acl_to_xattr(acl, value, size); 112 if (rc < 0) 113 goto out; 114 } 115 rc = __jfs_setxattr(tid, inode, ea_name, value, size, 0); 116 out: 117 kfree(value); 118 119 if (!rc) { 120 if (*p_acl && (*p_acl != JFS_ACL_NOT_CACHED)) 121 posix_acl_release(*p_acl); 122 *p_acl = posix_acl_dup(acl); 123 } 124 return rc; 125 } 126 127 static int jfs_check_acl(struct inode *inode, int mask) 128 { 129 struct jfs_inode_info *ji = JFS_IP(inode); 130 131 if (ji->i_acl == JFS_ACL_NOT_CACHED) { 132 struct posix_acl *acl = jfs_get_acl(inode, ACL_TYPE_ACCESS); 133 if (IS_ERR(acl)) 134 return PTR_ERR(acl); 135 posix_acl_release(acl); 136 } 137 138 if (ji->i_acl) 139 return posix_acl_permission(inode, ji->i_acl, mask); 140 return -EAGAIN; 141 } 142 143 int jfs_permission(struct inode *inode, int mask, struct nameidata *nd) 144 { 145 return generic_permission(inode, mask, jfs_check_acl); 146 } 147 148 int jfs_init_acl(tid_t tid, struct inode *inode, struct inode *dir) 149 { 150 struct posix_acl *acl = NULL; 151 struct posix_acl *clone; 152 mode_t mode; 153 int rc = 0; 154 155 if (S_ISLNK(inode->i_mode)) 156 return 0; 157 158 acl = jfs_get_acl(dir, ACL_TYPE_DEFAULT); 159 if (IS_ERR(acl)) 160 return PTR_ERR(acl); 161 162 if (acl) { 163 if (S_ISDIR(inode->i_mode)) { 164 rc = jfs_set_acl(tid, inode, ACL_TYPE_DEFAULT, acl); 165 if (rc) 166 goto cleanup; 167 } 168 clone = posix_acl_clone(acl, GFP_KERNEL); 169 if (!clone) { 170 rc = -ENOMEM; 171 goto cleanup; 172 } 173 mode = inode->i_mode; 174 rc = posix_acl_create_masq(clone, &mode); 175 if (rc >= 0) { 176 inode->i_mode = mode; 177 if (rc > 0) 178 rc = jfs_set_acl(tid, inode, ACL_TYPE_ACCESS, 179 clone); 180 } 181 posix_acl_release(clone); 182 cleanup: 183 posix_acl_release(acl); 184 } else 185 inode->i_mode &= ~current->fs->umask; 186 187 return rc; 188 } 189 190 static int jfs_acl_chmod(struct inode *inode) 191 { 192 struct posix_acl *acl, *clone; 193 int rc; 194 195 if (S_ISLNK(inode->i_mode)) 196 return -EOPNOTSUPP; 197 198 acl = jfs_get_acl(inode, ACL_TYPE_ACCESS); 199 if (IS_ERR(acl) || !acl) 200 return PTR_ERR(acl); 201 202 clone = posix_acl_clone(acl, GFP_KERNEL); 203 posix_acl_release(acl); 204 if (!clone) 205 return -ENOMEM; 206 207 rc = posix_acl_chmod_masq(clone, inode->i_mode); 208 if (!rc) { 209 tid_t tid = txBegin(inode->i_sb, 0); 210 down(&JFS_IP(inode)->commit_sem); 211 rc = jfs_set_acl(tid, inode, ACL_TYPE_ACCESS, clone); 212 if (!rc) 213 rc = txCommit(tid, 1, &inode, 0); 214 txEnd(tid); 215 up(&JFS_IP(inode)->commit_sem); 216 } 217 218 posix_acl_release(clone); 219 return rc; 220 } 221 222 int jfs_setattr(struct dentry *dentry, struct iattr *iattr) 223 { 224 struct inode *inode = dentry->d_inode; 225 int rc; 226 227 rc = inode_change_ok(inode, iattr); 228 if (rc) 229 return rc; 230 231 if ((iattr->ia_valid & ATTR_UID && iattr->ia_uid != inode->i_uid) || 232 (iattr->ia_valid & ATTR_GID && iattr->ia_gid != inode->i_gid)) { 233 if (DQUOT_TRANSFER(inode, iattr)) 234 return -EDQUOT; 235 } 236 237 rc = inode_setattr(inode, iattr); 238 239 if (!rc && (iattr->ia_valid & ATTR_MODE)) 240 rc = jfs_acl_chmod(inode); 241 242 return rc; 243 } 244