aboutsummaryrefslogtreecommitdiffstats
path: root/fs/generic_acl.c
diff options
context:
space:
mode:
authorAndreas Gruenbacher <agruen@suse.de>2006-09-29 05:01:34 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-09-29 12:18:24 -0400
commitf0c8bd164e1a0585d7e46896553136b4f488bd19 (patch)
tree75cbeec5113da7c20c6ee9ef09bdaea82014738a /fs/generic_acl.c
parent4e6fd33b75602ced4c5d43e99a10a1d13f33d4f4 (diff)
[PATCH] Generic infrastructure for acls
The patches solve the following problem: We want to grant access to devices based on who is logged in from where, etc. This includes switching back and forth between multiple user sessions, etc. Using ACLs to define device access for logged-in users gives us all the flexibility we need in order to fully solve the problem. Device special files nowadays usually live on tmpfs, hence tmpfs ACLs. Different distros have come up with solutions that solve the problem to different degrees: SUSE uses a resource manager which tracks login sessions and sets ACLs on device inodes as appropriate. RedHat uses pam_console, which changes the primary file ownership to the logged-in user. Others use a set of groups that users must be in in order to be granted the appropriate accesses. The freedesktop.org project plans to implement a combination of a console-tracker and a HAL-device-list based solution to grant access to devices to users, and more distros will likely follow this approach. These patches have first been posted here on 2 February 2005, and again on 8 January 2006. We have been shipping them in SLES9 and SLES10 with no problems reported. The previous submission is archived here: http://lkml.org/lkml/2006/1/8/229 http://lkml.org/lkml/2006/1/8/230 http://lkml.org/lkml/2006/1/8/231 This patch: Add some infrastructure for access control lists on in-memory filesystems such as tmpfs. Signed-off-by: Andreas Gruenbacher <agruen@suse.de> Cc: Hugh Dickins <hugh@veritas.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'fs/generic_acl.c')
-rw-r--r--fs/generic_acl.c197
1 files changed, 197 insertions, 0 deletions
diff --git a/fs/generic_acl.c b/fs/generic_acl.c
new file mode 100644
index 000000000000..9ccb78947171
--- /dev/null
+++ b/fs/generic_acl.c
@@ -0,0 +1,197 @@
1/*
2 * fs/generic_acl.c
3 *
4 * (C) 2005 Andreas Gruenbacher <agruen@suse.de>
5 *
6 * This file is released under the GPL.
7 */
8
9#include <linux/sched.h>
10#include <linux/fs.h>
11#include <linux/generic_acl.h>
12
13/**
14 * generic_acl_list - Generic xattr_handler->list() operation
15 * @ops: Filesystem specific getacl and setacl callbacks
16 */
17size_t
18generic_acl_list(struct inode *inode, struct generic_acl_operations *ops,
19 int type, char *list, size_t list_size)
20{
21 struct posix_acl *acl;
22 const char *name;
23 size_t size;
24
25 acl = ops->getacl(inode, type);
26 if (!acl)
27 return 0;
28 posix_acl_release(acl);
29
30 switch(type) {
31 case ACL_TYPE_ACCESS:
32 name = POSIX_ACL_XATTR_ACCESS;
33 break;
34
35 case ACL_TYPE_DEFAULT:
36 name = POSIX_ACL_XATTR_DEFAULT;
37 break;
38
39 default:
40 return 0;
41 }
42 size = strlen(name) + 1;
43 if (list && size <= list_size)
44 memcpy(list, name, size);
45 return size;
46}
47
48/**
49 * generic_acl_get - Generic xattr_handler->get() operation
50 * @ops: Filesystem specific getacl and setacl callbacks
51 */
52int
53generic_acl_get(struct inode *inode, struct generic_acl_operations *ops,
54 int type, void *buffer, size_t size)
55{
56 struct posix_acl *acl;
57 int error;
58
59 acl = ops->getacl(inode, type);
60 if (!acl)
61 return -ENODATA;
62 error = posix_acl_to_xattr(acl, buffer, size);
63 posix_acl_release(acl);
64
65 return error;
66}
67
68/**
69 * generic_acl_set - Generic xattr_handler->set() operation
70 * @ops: Filesystem specific getacl and setacl callbacks
71 */
72int
73generic_acl_set(struct inode *inode, struct generic_acl_operations *ops,
74 int type, const void *value, size_t size)
75{
76 struct posix_acl *acl = NULL;
77 int error;
78
79 if (S_ISLNK(inode->i_mode))
80 return -EOPNOTSUPP;
81 if (current->fsuid != inode->i_uid && !capable(CAP_FOWNER))
82 return -EPERM;
83 if (value) {
84 acl = posix_acl_from_xattr(value, size);
85 if (IS_ERR(acl))
86 return PTR_ERR(acl);
87 }
88 if (acl) {
89 mode_t mode;
90
91 error = posix_acl_valid(acl);
92 if (error)
93 goto failed;
94 switch(type) {
95 case ACL_TYPE_ACCESS:
96 mode = inode->i_mode;
97 error = posix_acl_equiv_mode(acl, &mode);
98 if (error < 0)
99 goto failed;
100 inode->i_mode = mode;
101 if (error == 0) {
102 posix_acl_release(acl);
103 acl = NULL;
104 }
105 break;
106
107 case ACL_TYPE_DEFAULT:
108 if (!S_ISDIR(inode->i_mode)) {
109 error = -EINVAL;
110 goto failed;
111 }
112 break;
113 }
114 }
115 ops->setacl(inode, type, acl);
116 error = 0;
117failed:
118 posix_acl_release(acl);
119 return error;
120}
121
122/**
123 * generic_acl_init - Take care of acl inheritance at @inode create time
124 * @ops: Filesystem specific getacl and setacl callbacks
125 *
126 * Files created inside a directory with a default ACL inherit the
127 * directory's default ACL.
128 */
129int
130generic_acl_init(struct inode *inode, struct inode *dir,
131 struct generic_acl_operations *ops)
132{
133 struct posix_acl *acl = NULL;
134 mode_t mode = inode->i_mode;
135 int error;
136
137 inode->i_mode = mode & ~current->fs->umask;
138 if (!S_ISLNK(inode->i_mode))
139 acl = ops->getacl(dir, ACL_TYPE_DEFAULT);
140 if (acl) {
141 struct posix_acl *clone;
142
143 if (S_ISDIR(inode->i_mode)) {
144 clone = posix_acl_clone(acl, GFP_KERNEL);
145 error = -ENOMEM;
146 if (!clone)
147 goto cleanup;
148 ops->setacl(inode, ACL_TYPE_DEFAULT, clone);
149 posix_acl_release(clone);
150 }
151 clone = posix_acl_clone(acl, GFP_KERNEL);
152 error = -ENOMEM;
153 if (!clone)
154 goto cleanup;
155 error = posix_acl_create_masq(clone, &mode);
156 if (error >= 0) {
157 inode->i_mode = mode;
158 if (error > 0)
159 ops->setacl(inode, ACL_TYPE_ACCESS, clone);
160 }
161 posix_acl_release(clone);
162 }
163 error = 0;
164
165cleanup:
166 posix_acl_release(acl);
167 return error;
168}
169
170/**
171 * generic_acl_chmod - change the access acl of @inode upon chmod()
172 * @ops: FIlesystem specific getacl and setacl callbacks
173 *
174 * A chmod also changes the permissions of the owner, group/mask, and
175 * other ACL entries.
176 */
177int
178generic_acl_chmod(struct inode *inode, struct generic_acl_operations *ops)
179{
180 struct posix_acl *acl, *clone;
181 int error = 0;
182
183 if (S_ISLNK(inode->i_mode))
184 return -EOPNOTSUPP;
185 acl = ops->getacl(inode, ACL_TYPE_ACCESS);
186 if (acl) {
187 clone = posix_acl_clone(acl, GFP_KERNEL);
188 posix_acl_release(acl);
189 if (!clone)
190 return -ENOMEM;
191 error = posix_acl_chmod_masq(clone, inode->i_mode);
192 if (!error)
193 ops->setacl(inode, ACL_TYPE_ACCESS, clone);
194 posix_acl_release(clone);
195 }
196 return error;
197}