File Index
Files and directories
Sign in
FILE

acl.c

File preview with syntax highlighting.

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