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 JFS_IP(inode)->mode2 = (JFS_IP(inode)->mode2 & 0xffff0000) | 188 inode->i_mode; 189 190 return rc; 191 } 192 193 static int jfs_acl_chmod(struct inode *inode) 194 { 195 struct posix_acl *acl, *clone; 196 int rc; 197 198 if (S_ISLNK(inode->i_mode)) 199 return -EOPNOTSUPP; 200 201 acl = jfs_get_acl(inode, ACL_TYPE_ACCESS); 202 if (IS_ERR(acl) || !acl) 203 return PTR_ERR(acl); 204 205 clone = posix_acl_clone(acl, GFP_KERNEL); 206 posix_acl_release(acl); 207 if (!clone) 208 return -ENOMEM; 209 210 rc = posix_acl_chmod_masq(clone, inode->i_mode); 211 if (!rc) { 212 tid_t tid = txBegin(inode->i_sb, 0); 213 mutex_lock(&JFS_IP(inode)->commit_mutex); 214 rc = jfs_set_acl(tid, inode, ACL_TYPE_ACCESS, clone); 215 if (!rc) 216 rc = txCommit(tid, 1, &inode, 0); 217 txEnd(tid); 218 mutex_unlock(&JFS_IP(inode)->commit_mutex); 219 } 220 221 posix_acl_release(clone); 222 return rc; 223 } 224 225 int jfs_setattr(struct dentry *dentry, struct iattr *iattr) 226 { 227 struct inode *inode = dentry->d_inode; 228 int rc; 229 230 rc = inode_change_ok(inode, iattr); 231 if (rc) 232 return rc; 233 234 if ((iattr->ia_valid & ATTR_UID && iattr->ia_uid != inode->i_uid) || 235 (iattr->ia_valid & ATTR_GID && iattr->ia_gid != inode->i_gid)) { 236 if (DQUOT_TRANSFER(inode, iattr)) 237 return -EDQUOT; 238 } 239 240 rc = inode_setattr(inode, iattr); 241 242 if (!rc && (iattr->ia_valid & ATTR_MODE)) 243 rc = jfs_acl_chmod(inode); 244 245 return rc; 246 } 247