1/* -*- mode: c; c-basic-offset: 8; -*- 2 * vim: noexpandtab sw=8 ts=8 sts=0: 3 * 4 * acl.c 5 * 6 * Copyright (C) 2004, 2008 Oracle. All rights reserved. 7 * 8 * CREDITS: 9 * Lots of code in this file is copy from linux/fs/ext3/acl.c. 10 * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de> 11 * 12 * This program is free software; you can redistribute it and/or 13 * modify it under the terms of the GNU General Public 14 * License version 2 as published by the Free Software Foundation. 15 * 16 * This program is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 * General Public License for more details. 20 */ 21 22#include <linux/init.h> 23#include <linux/module.h> 24#include <linux/slab.h> 25#include <linux/string.h> 26 27#include <cluster/masklog.h> 28 29#include "ocfs2.h" 30#include "alloc.h" 31#include "dlmglue.h" 32#include "file.h" 33#include "inode.h" 34#include "journal.h" 35#include "ocfs2_fs.h" 36 37#include "xattr.h" 38#include "acl.h" 39 40/* 41 * Convert from xattr value to acl struct. 42 */ 43static struct posix_acl *ocfs2_acl_from_xattr(const void *value, size_t size) 44{ 45 int n, count; 46 struct posix_acl *acl; 47 48 if (!value) 49 return NULL; 50 if (size < sizeof(struct posix_acl_entry)) 51 return ERR_PTR(-EINVAL); 52 53 count = size / sizeof(struct posix_acl_entry); 54 55 acl = posix_acl_alloc(count, GFP_NOFS); 56 if (!acl) 57 return ERR_PTR(-ENOMEM); 58 for (n = 0; n < count; n++) { 59 struct ocfs2_acl_entry *entry = 60 (struct ocfs2_acl_entry *)value; 61 62 acl->a_entries[n].e_tag = le16_to_cpu(entry->e_tag); 63 acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm); 64 switch(acl->a_entries[n].e_tag) { 65 case ACL_USER: 66 acl->a_entries[n].e_uid = 67 make_kuid(&init_user_ns, 68 le32_to_cpu(entry->e_id)); 69 break; 70 case ACL_GROUP: 71 acl->a_entries[n].e_gid = 72 make_kgid(&init_user_ns, 73 le32_to_cpu(entry->e_id)); 74 break; 75 default: 76 break; 77 } 78 value += sizeof(struct posix_acl_entry); 79 80 } 81 return acl; 82} 83 84/* 85 * Convert acl struct to xattr value. 86 */ 87static void *ocfs2_acl_to_xattr(const struct posix_acl *acl, size_t *size) 88{ 89 struct ocfs2_acl_entry *entry = NULL; 90 char *ocfs2_acl; 91 size_t n; 92 93 *size = acl->a_count * sizeof(struct posix_acl_entry); 94 95 ocfs2_acl = kmalloc(*size, GFP_NOFS); 96 if (!ocfs2_acl) 97 return ERR_PTR(-ENOMEM); 98 99 entry = (struct ocfs2_acl_entry *)ocfs2_acl; 100 for (n = 0; n < acl->a_count; n++, entry++) { 101 entry->e_tag = cpu_to_le16(acl->a_entries[n].e_tag); 102 entry->e_perm = cpu_to_le16(acl->a_entries[n].e_perm); 103 switch(acl->a_entries[n].e_tag) { 104 case ACL_USER: 105 entry->e_id = cpu_to_le32( 106 from_kuid(&init_user_ns, 107 acl->a_entries[n].e_uid)); 108 break; 109 case ACL_GROUP: 110 entry->e_id = cpu_to_le32( 111 from_kgid(&init_user_ns, 112 acl->a_entries[n].e_gid)); 113 break; 114 default: 115 entry->e_id = cpu_to_le32(ACL_UNDEFINED_ID); 116 break; 117 } 118 } 119 return ocfs2_acl; 120} 121 122static struct posix_acl *ocfs2_get_acl_nolock(struct inode *inode, 123 int type, 124 struct buffer_head *di_bh) 125{ 126 int name_index; 127 char *value = NULL; 128 struct posix_acl *acl; 129 int retval; 130 131 switch (type) { 132 case ACL_TYPE_ACCESS: 133 name_index = OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS; 134 break; 135 case ACL_TYPE_DEFAULT: 136 name_index = OCFS2_XATTR_INDEX_POSIX_ACL_DEFAULT; 137 break; 138 default: 139 return ERR_PTR(-EINVAL); 140 } 141 142 retval = ocfs2_xattr_get_nolock(inode, di_bh, name_index, "", NULL, 0); 143 if (retval > 0) { 144 value = kmalloc(retval, GFP_NOFS); 145 if (!value) 146 return ERR_PTR(-ENOMEM); 147 retval = ocfs2_xattr_get_nolock(inode, di_bh, name_index, 148 "", value, retval); 149 } 150 151 if (retval > 0) 152 acl = ocfs2_acl_from_xattr(value, retval); 153 else if (retval == -ENODATA || retval == 0) 154 acl = NULL; 155 else 156 acl = ERR_PTR(retval); 157 158 kfree(value); 159 160 return acl; 161} 162 163/* 164 * Helper function to set i_mode in memory and disk. Some call paths 165 * will not have di_bh or a journal handle to pass, in which case it 166 * will create it's own. 167 */ 168static int ocfs2_acl_set_mode(struct inode *inode, struct buffer_head *di_bh, 169 handle_t *handle, umode_t new_mode) 170{ 171 int ret, commit_handle = 0; 172 struct ocfs2_dinode *di; 173 174 if (di_bh == NULL) { 175 ret = ocfs2_read_inode_block(inode, &di_bh); 176 if (ret) { 177 mlog_errno(ret); 178 goto out; 179 } 180 } else 181 get_bh(di_bh); 182 183 if (handle == NULL) { 184 handle = ocfs2_start_trans(OCFS2_SB(inode->i_sb), 185 OCFS2_INODE_UPDATE_CREDITS); 186 if (IS_ERR(handle)) { 187 ret = PTR_ERR(handle); 188 mlog_errno(ret); 189 goto out_brelse; 190 } 191 192 commit_handle = 1; 193 } 194 195 di = (struct ocfs2_dinode *)di_bh->b_data; 196 ret = ocfs2_journal_access_di(handle, INODE_CACHE(inode), di_bh, 197 OCFS2_JOURNAL_ACCESS_WRITE); 198 if (ret) { 199 mlog_errno(ret); 200 goto out_commit; 201 } 202 203 inode->i_mode = new_mode; 204 inode->i_ctime = CURRENT_TIME; 205 di->i_mode = cpu_to_le16(inode->i_mode); 206 di->i_ctime = cpu_to_le64(inode->i_ctime.tv_sec); 207 di->i_ctime_nsec = cpu_to_le32(inode->i_ctime.tv_nsec); 208 ocfs2_update_inode_fsync_trans(handle, inode, 0); 209 210 ocfs2_journal_dirty(handle, di_bh); 211 212out_commit: 213 if (commit_handle) 214 ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle); 215out_brelse: 216 brelse(di_bh); 217out: 218 return ret; 219} 220 221/* 222 * Set the access or default ACL of an inode. 223 */ 224int ocfs2_set_acl(handle_t *handle, 225 struct inode *inode, 226 struct buffer_head *di_bh, 227 int type, 228 struct posix_acl *acl, 229 struct ocfs2_alloc_context *meta_ac, 230 struct ocfs2_alloc_context *data_ac) 231{ 232 int name_index; 233 void *value = NULL; 234 size_t size = 0; 235 int ret; 236 237 if (S_ISLNK(inode->i_mode)) 238 return -EOPNOTSUPP; 239 240 switch (type) { 241 case ACL_TYPE_ACCESS: 242 name_index = OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS; 243 if (acl) { 244 umode_t mode = inode->i_mode; 245 ret = posix_acl_equiv_mode(acl, &mode); 246 if (ret < 0) 247 return ret; 248 249 if (ret == 0) 250 acl = NULL; 251 252 ret = ocfs2_acl_set_mode(inode, di_bh, 253 handle, mode); 254 if (ret) 255 return ret; 256 } 257 break; 258 case ACL_TYPE_DEFAULT: 259 name_index = OCFS2_XATTR_INDEX_POSIX_ACL_DEFAULT; 260 if (!S_ISDIR(inode->i_mode)) 261 return acl ? -EACCES : 0; 262 break; 263 default: 264 return -EINVAL; 265 } 266 267 if (acl) { 268 value = ocfs2_acl_to_xattr(acl, &size); 269 if (IS_ERR(value)) 270 return (int)PTR_ERR(value); 271 } 272 273 if (handle) 274 ret = ocfs2_xattr_set_handle(handle, inode, di_bh, name_index, 275 "", value, size, 0, 276 meta_ac, data_ac); 277 else 278 ret = ocfs2_xattr_set(inode, name_index, "", value, size, 0); 279 280 kfree(value); 281 282 return ret; 283} 284 285int ocfs2_iop_set_acl(struct inode *inode, struct posix_acl *acl, int type) 286{ 287 return ocfs2_set_acl(NULL, inode, NULL, type, acl, NULL, NULL); 288} 289 290struct posix_acl *ocfs2_iop_get_acl(struct inode *inode, int type) 291{ 292 struct ocfs2_super *osb; 293 struct buffer_head *di_bh = NULL; 294 struct posix_acl *acl; 295 int ret = -EAGAIN; 296 297 osb = OCFS2_SB(inode->i_sb); 298 if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL)) 299 return NULL; 300 301 ret = ocfs2_read_inode_block(inode, &di_bh); 302 if (ret < 0) 303 return ERR_PTR(ret); 304 305 acl = ocfs2_get_acl_nolock(inode, type, di_bh); 306 307 brelse(di_bh); 308 309 return acl; 310} 311 312int ocfs2_acl_chmod(struct inode *inode, struct buffer_head *bh) 313{ 314 struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); 315 struct posix_acl *acl; 316 int ret; 317 318 if (S_ISLNK(inode->i_mode)) 319 return -EOPNOTSUPP; 320 321 if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL)) 322 return 0; 323 324 acl = ocfs2_get_acl_nolock(inode, ACL_TYPE_ACCESS, bh); 325 if (IS_ERR(acl) || !acl) 326 return PTR_ERR(acl); 327 ret = __posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode); 328 if (ret) 329 return ret; 330 ret = ocfs2_set_acl(NULL, inode, NULL, ACL_TYPE_ACCESS, 331 acl, NULL, NULL); 332 posix_acl_release(acl); 333 return ret; 334} 335 336/* 337 * Initialize the ACLs of a new inode. If parent directory has default ACL, 338 * then clone to new inode. Called from ocfs2_mknod. 339 */ 340int ocfs2_init_acl(handle_t *handle, 341 struct inode *inode, 342 struct inode *dir, 343 struct buffer_head *di_bh, 344 struct buffer_head *dir_bh, 345 struct ocfs2_alloc_context *meta_ac, 346 struct ocfs2_alloc_context *data_ac) 347{ 348 struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); 349 struct posix_acl *acl = NULL; 350 int ret = 0, ret2; 351 umode_t mode; 352 353 if (!S_ISLNK(inode->i_mode)) { 354 if (osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) { 355 acl = ocfs2_get_acl_nolock(dir, ACL_TYPE_DEFAULT, 356 dir_bh); 357 if (IS_ERR(acl)) 358 return PTR_ERR(acl); 359 } 360 if (!acl) { 361 mode = inode->i_mode & ~current_umask(); 362 ret = ocfs2_acl_set_mode(inode, di_bh, handle, mode); 363 if (ret) { 364 mlog_errno(ret); 365 goto cleanup; 366 } 367 } 368 } 369 if ((osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) && acl) { 370 if (S_ISDIR(inode->i_mode)) { 371 ret = ocfs2_set_acl(handle, inode, di_bh, 372 ACL_TYPE_DEFAULT, acl, 373 meta_ac, data_ac); 374 if (ret) 375 goto cleanup; 376 } 377 mode = inode->i_mode; 378 ret = __posix_acl_create(&acl, GFP_NOFS, &mode); 379 if (ret < 0) 380 return ret; 381 382 ret2 = ocfs2_acl_set_mode(inode, di_bh, handle, mode); 383 if (ret2) { 384 mlog_errno(ret2); 385 ret = ret2; 386 goto cleanup; 387 } 388 if (ret > 0) { 389 ret = ocfs2_set_acl(handle, inode, 390 di_bh, ACL_TYPE_ACCESS, 391 acl, meta_ac, data_ac); 392 } 393 } 394cleanup: 395 posix_acl_release(acl); 396 return ret; 397} 398