~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/fs/ocfs2/acl.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php