1/*
2 * Copyright (c) 2002 Silicon Graphics, Inc.  All Rights Reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of version 2 of the GNU General Public License as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it would be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 *
12 * Further, this software is distributed without any warranty that it is
13 * free of the rightful claim of any third person regarding infringement
14 * or the like.  Any license provided herein, whether implied or
15 * otherwise, applies only to this software file.  Patent licenses, if
16 * any, provided herein do not apply to combinations of this program with
17 * other software, or any other product whatsoever.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write the Free Software Foundation, Inc., 59
21 * Temple Place - Suite 330, Boston MA 02111-1307, USA.
22 *
23 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
24 * Mountain View, CA  94043, or:
25 *
26 * http://www.sgi.com
27 *
28 * For further information regarding this notice, see:
29 *
30 * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
31 */
32
33#include "xfs.h"
34
35STATIC int xfs_cap_allow_set(xfs_vnode_t *);
36
37
38/*
39 * Test for existence of capability attribute as efficiently as possible.
40 */
41int
42xfs_cap_vhascap(
43	xfs_vnode_t	*vp)
44{
45	int		error;
46	int		len = sizeof(xfs_cap_set_t);
47	int		flags = ATTR_KERNOVAL|ATTR_ROOT;
48
49	XVOP_ATTR_GET(vp, SGI_CAP_LINUX, NULL, &len, flags, sys_cred, error);
50	return (error == 0);
51}
52
53/*
54 * Convert from extended attribute representation to in-memory for XFS.
55 */
56STATIC int
57posix_cap_xattr_to_xfs(
58	posix_cap_xattr		*src,
59	size_t			size,
60	xfs_cap_set_t		*dest)
61{
62	if (!src || !dest)
63		return EINVAL;
64
65	if (src->c_version != cpu_to_le32(POSIX_CAP_XATTR_VERSION))
66		return EINVAL;
67	if (src->c_abiversion != cpu_to_le32(_LINUX_CAPABILITY_VERSION))
68		return EINVAL;
69
70	if (size < sizeof(posix_cap_xattr))
71		return EINVAL;
72
73	ASSERT(sizeof(dest->cap_effective) == sizeof(src->c_effective));
74
75	dest->cap_effective	= src->c_effective;
76	dest->cap_permitted	= src->c_permitted;
77	dest->cap_inheritable	= src->c_inheritable;
78
79	return 0;
80}
81
82/*
83 * Convert from in-memory XFS to extended attribute representation.
84 */
85STATIC int
86posix_cap_xfs_to_xattr(
87	xfs_cap_set_t		*src,
88	posix_cap_xattr		*xattr_cap,
89	size_t			size)
90{
91	size_t			new_size = posix_cap_xattr_size();
92
93	if (size < new_size)
94		return -ERANGE;
95
96	ASSERT(sizeof(xattr_cap->c_effective) == sizeof(src->cap_effective));
97
98	xattr_cap->c_version	= cpu_to_le32(POSIX_CAP_XATTR_VERSION);
99	xattr_cap->c_abiversion	= cpu_to_le32(_LINUX_CAPABILITY_VERSION);
100	xattr_cap->c_effective	= src->cap_effective;
101	xattr_cap->c_permitted	= src->cap_permitted;
102	xattr_cap->c_inheritable= src->cap_inheritable;
103
104	return new_size;
105}
106
107int
108xfs_cap_vget(
109	xfs_vnode_t	*vp,
110	void		*cap,
111	size_t		size)
112{
113	int		error;
114	int		len = sizeof(xfs_cap_set_t);
115	int		flags = ATTR_ROOT;
116	xfs_cap_set_t	xfs_cap = { 0 };
117	posix_cap_xattr	*xattr_cap = cap;
118	char		*data = (char *)&xfs_cap;
119
120	VN_HOLD(vp);
121	if ((error = _MAC_VACCESS(vp, NULL, VREAD)))
122		goto out;
123
124	if (!size) {
125		flags |= ATTR_KERNOVAL;
126		data = NULL;
127	}
128	XVOP_ATTR_GET(vp, SGI_CAP_LINUX, data, &len, flags, sys_cred, error);
129	if (error)
130		goto out;
131	ASSERT(len == sizeof(xfs_cap_set_t));
132
133	error = (size)? -posix_cap_xattr_size() :
134			-posix_cap_xfs_to_xattr(&xfs_cap, xattr_cap, size);
135out:
136	VN_RELE(vp);
137	return -error;
138}
139
140int
141xfs_cap_vremove(
142	xfs_vnode_t	*vp)
143{
144	int		error;
145
146	VN_HOLD(vp);
147	error = xfs_cap_allow_set(vp);
148	if (!error) {
149		XVOP_ATTR_REMOVE(vp, SGI_CAP_LINUX, ATTR_ROOT, sys_cred, error);
150		if (error == ENOATTR)
151			error = 0;	/* 'scool */
152	}
153	VN_RELE(vp);
154	return -error;
155}
156
157int
158xfs_cap_vset(
159	xfs_vnode_t		*vp,
160	void			*cap,
161	size_t			size)
162{
163	posix_cap_xattr		*xattr_cap = cap;
164	xfs_cap_set_t		xfs_cap;
165	int			error;
166
167	if (!cap)
168		return -EINVAL;
169
170	error = posix_cap_xattr_to_xfs(xattr_cap, size, &xfs_cap);
171	if (error)
172		return -error;
173
174	VN_HOLD(vp);
175	error = xfs_cap_allow_set(vp);
176	if (error)
177		goto out;
178
179	XVOP_ATTR_SET(vp, SGI_CAP_LINUX, (char *)&xfs_cap,
180			sizeof(xfs_cap_set_t), ATTR_ROOT, sys_cred, error);
181out:
182	VN_RELE(vp);
183	return -error;
184}
185
186STATIC int
187xfs_cap_allow_set(
188	xfs_vnode_t	*vp)
189{
190	vattr_t		va;
191	int		error;
192
193	if (vp->v_vfsp->vfs_flag & VFS_RDONLY)
194		return EROFS;
195	if (vp->v_inode.i_flags & (S_IMMUTABLE|S_APPEND))
196		return EPERM;
197	if ((error = _MAC_VACCESS(vp, NULL, VWRITE)))
198		return error;
199	va.va_mask = XFS_AT_UID;
200	XVOP_GETATTR(vp, &va, 0, NULL, error);
201	if (error)
202		return error;
203	if (va.va_uid != current->fsuid && !capable(CAP_FOWNER))
204		return EPERM;
205	return error;
206}
207
208