Blame view
fs/xattr.c
23.5 KB
1da177e4c Linux-2.6.12-rc2 |
1 2 3 4 5 6 7 8 9 10 11 |
/* File: fs/xattr.c Extended attribute handling. Copyright (C) 2001 by Andreas Gruenbacher <a.gruenbacher@computer.org> Copyright (C) 2001 SGI - Silicon Graphics, Inc <linux-xfs@oss.sgi.com> Copyright (c) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com> */ #include <linux/fs.h> #include <linux/slab.h> |
1da177e4c Linux-2.6.12-rc2 |
12 13 |
#include <linux/file.h> #include <linux/xattr.h> |
18f335aff [PATCH] r/o bind ... |
14 |
#include <linux/mount.h> |
1da177e4c Linux-2.6.12-rc2 |
15 16 |
#include <linux/namei.h> #include <linux/security.h> |
c7b87de23 evm: evm_inode_po... |
17 |
#include <linux/evm.h> |
1da177e4c Linux-2.6.12-rc2 |
18 |
#include <linux/syscalls.h> |
630d9c472 fs: reduce the us... |
19 |
#include <linux/export.h> |
0eeca2830 [PATCH] inotify |
20 |
#include <linux/fsnotify.h> |
73241ccca [PATCH] Collect m... |
21 |
#include <linux/audit.h> |
0d08d7b7e fs/xattr.c:listxa... |
22 |
#include <linux/vmalloc.h> |
2f6f0654a userns: Convert v... |
23 |
#include <linux/posix_acl_xattr.h> |
1da177e4c Linux-2.6.12-rc2 |
24 |
|
0d08d7b7e fs/xattr.c:listxa... |
25 |
#include <asm/uaccess.h> |
5be196e5f [PATCH] add vfs_*... |
26 |
|
e0ad7b073 [PATCH] move xatt... |
27 28 29 30 31 32 33 34 35 36 37 38 |
/* * Check permissions for extended attribute access. This is a bit complicated * because different namespaces have very different rules. */ static int xattr_permission(struct inode *inode, const char *name, int mask) { /* * We can never set or remove an extended attribute on a read-only * filesystem or on an immutable / append-only inode. */ if (mask & MAY_WRITE) { |
e0ad7b073 [PATCH] move xatt... |
39 40 41 42 43 44 45 46 47 48 49 50 51 |
if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) return -EPERM; } /* * No restriction for security.* and system.* from the VFS. Decision * on these is left to the underlying filesystem / security module. */ if (!strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) || !strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN)) return 0; /* |
55b23bde1 xattr: Fix error ... |
52 |
* The trusted.* namespace can only be accessed by privileged users. |
e0ad7b073 [PATCH] move xatt... |
53 |
*/ |
55b23bde1 xattr: Fix error ... |
54 55 56 57 58 |
if (!strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN)) { if (!capable(CAP_SYS_ADMIN)) return (mask & MAY_WRITE) ? -EPERM : -ENODATA; return 0; } |
e0ad7b073 [PATCH] move xatt... |
59 |
|
55b23bde1 xattr: Fix error ... |
60 61 |
/* * In the user.* namespace, only regular files and directories can have |
f1f2d8713 [PATCH] Fix user.... |
62 |
* extended attributes. For sticky directories, only the owner and |
55b23bde1 xattr: Fix error ... |
63 |
* privileged users can write attributes. |
f1f2d8713 [PATCH] Fix user.... |
64 |
*/ |
e0ad7b073 [PATCH] move xatt... |
65 |
if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) { |
f1f2d8713 [PATCH] Fix user.... |
66 |
if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode)) |
55b23bde1 xattr: Fix error ... |
67 |
return (mask & MAY_WRITE) ? -EPERM : -ENODATA; |
f1f2d8713 [PATCH] Fix user.... |
68 |
if (S_ISDIR(inode->i_mode) && (inode->i_mode & S_ISVTX) && |
2e1496707 userns: rename is... |
69 |
(mask & MAY_WRITE) && !inode_owner_or_capable(inode)) |
e0ad7b073 [PATCH] move xatt... |
70 71 |
return -EPERM; } |
f419a2e3b [PATCH] kill name... |
72 |
return inode_permission(inode, mask); |
e0ad7b073 [PATCH] move xatt... |
73 |
} |
b1ab7e4b2 VFS: Factor out p... |
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
/** * __vfs_setxattr_noperm - perform setxattr operation without performing * permission checks. * * @dentry - object to perform setxattr on * @name - xattr name to set * @value - value to set @name to * @size - size of @value * @flags - flags to pass into filesystem operations * * returns the result of the internal setxattr or setsecurity operations. * * This function requires the caller to lock the inode's i_mutex before it * is executed. It also assumes that the caller will make the appropriate * permission checks. */ int __vfs_setxattr_noperm(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) |
5be196e5f [PATCH] add vfs_*... |
92 93 |
{ struct inode *inode = dentry->d_inode; |
b1ab7e4b2 VFS: Factor out p... |
94 |
int error = -EOPNOTSUPP; |
69b457329 Cache xattr secur... |
95 96 |
int issec = !strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN); |
e0ad7b073 [PATCH] move xatt... |
97 |
|
69b457329 Cache xattr secur... |
98 99 |
if (issec) inode->i_flags &= ~S_NOSEC; |
5be196e5f [PATCH] add vfs_*... |
100 101 102 103 104 105 106 |
if (inode->i_op->setxattr) { error = inode->i_op->setxattr(dentry, name, value, size, flags); if (!error) { fsnotify_xattr(dentry); security_inode_post_setxattr(dentry, name, value, size, flags); } |
69b457329 Cache xattr secur... |
107 |
} else if (issec) { |
e0ad7b073 [PATCH] move xatt... |
108 |
const char *suffix = name + XATTR_SECURITY_PREFIX_LEN; |
5be196e5f [PATCH] add vfs_*... |
109 110 111 112 113 |
error = security_inode_setsecurity(inode, suffix, value, size, flags); if (!error) fsnotify_xattr(dentry); } |
b1ab7e4b2 VFS: Factor out p... |
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
return error; } int vfs_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { struct inode *inode = dentry->d_inode; int error; error = xattr_permission(inode, name, MAY_WRITE); if (error) return error; mutex_lock(&inode->i_mutex); error = security_inode_setxattr(dentry, name, value, size, flags); if (error) goto out; error = __vfs_setxattr_noperm(dentry, name, value, size, flags); |
5be196e5f [PATCH] add vfs_*... |
136 137 138 139 140 141 142 |
out: mutex_unlock(&inode->i_mutex); return error; } EXPORT_SYMBOL_GPL(vfs_setxattr); ssize_t |
424925940 VFS/Security: Rew... |
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
xattr_getsecurity(struct inode *inode, const char *name, void *value, size_t size) { void *buffer = NULL; ssize_t len; if (!value || !size) { len = security_inode_getsecurity(inode, name, &buffer, false); goto out_noalloc; } len = security_inode_getsecurity(inode, name, &buffer, true); if (len < 0) return len; if (size < len) { len = -ERANGE; goto out; } memcpy(value, buffer, len); out: security_release_secctx(buffer, len); out_noalloc: return len; } EXPORT_SYMBOL_GPL(xattr_getsecurity); |
1601fbad2 xattr: define vfs... |
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
/* * vfs_getxattr_alloc - allocate memory, if necessary, before calling getxattr * * Allocate memory, if not already allocated, or re-allocate correct size, * before retrieving the extended attribute. * * Returns the result of alloc, if failed, or the getxattr operation. */ ssize_t vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value, size_t xattr_size, gfp_t flags) { struct inode *inode = dentry->d_inode; char *value = *xattr_value; int error; error = xattr_permission(inode, name, MAY_READ); if (error) return error; if (!inode->i_op->getxattr) return -EOPNOTSUPP; error = inode->i_op->getxattr(dentry, name, NULL, 0); if (error < 0) return error; if (!value || (error > xattr_size)) { value = krealloc(*xattr_value, error + 1, flags); if (!value) return -ENOMEM; memset(value, 0, error + 1); } error = inode->i_op->getxattr(dentry, name, value, error); *xattr_value = value; return error; } /* Compare an extended attribute value with the given value */ int vfs_xattr_cmp(struct dentry *dentry, const char *xattr_name, const char *value, size_t size, gfp_t flags) { char *xattr_value = NULL; int rc; rc = vfs_getxattr_alloc(dentry, xattr_name, &xattr_value, 0, flags); if (rc < 0) return rc; if ((rc != size) || (memcmp(xattr_value, value, rc) != 0)) rc = -EINVAL; else rc = 0; kfree(xattr_value); return rc; } |
424925940 VFS/Security: Rew... |
225 |
ssize_t |
8f0cfa52a xattr: add missin... |
226 |
vfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size) |
5be196e5f [PATCH] add vfs_*... |
227 228 229 |
{ struct inode *inode = dentry->d_inode; int error; |
e0ad7b073 [PATCH] move xatt... |
230 231 232 |
error = xattr_permission(inode, name, MAY_READ); if (error) return error; |
5be196e5f [PATCH] add vfs_*... |
233 234 235 |
error = security_inode_getxattr(dentry, name); if (error) return error; |
5be196e5f [PATCH] add vfs_*... |
236 |
if (!strncmp(name, XATTR_SECURITY_PREFIX, |
e0ad7b073 [PATCH] move xatt... |
237 238 |
XATTR_SECURITY_PREFIX_LEN)) { const char *suffix = name + XATTR_SECURITY_PREFIX_LEN; |
424925940 VFS/Security: Rew... |
239 |
int ret = xattr_getsecurity(inode, suffix, value, size); |
5be196e5f [PATCH] add vfs_*... |
240 241 242 243 |
/* * Only overwrite the return value if a security module * is actually active. */ |
4bea58053 VFS: Reorder vfs_... |
244 245 246 |
if (ret == -EOPNOTSUPP) goto nolsm; return ret; |
5be196e5f [PATCH] add vfs_*... |
247 |
} |
4bea58053 VFS: Reorder vfs_... |
248 249 250 251 252 |
nolsm: if (inode->i_op->getxattr) error = inode->i_op->getxattr(dentry, name, value, size); else error = -EOPNOTSUPP; |
5be196e5f [PATCH] add vfs_*... |
253 254 255 256 |
return error; } EXPORT_SYMBOL_GPL(vfs_getxattr); |
659564c8a [PATCH] Introduce... |
257 258 259 260 261 262 263 264 265 |
ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size) { ssize_t error; error = security_inode_listxattr(d); if (error) return error; error = -EOPNOTSUPP; |
acfa4380e inode->i_op is ne... |
266 |
if (d->d_inode->i_op->listxattr) { |
659564c8a [PATCH] Introduce... |
267 268 269 270 271 272 273 274 275 |
error = d->d_inode->i_op->listxattr(d, list, size); } else { error = security_inode_listsecurity(d->d_inode, list, size); if (size && error > size) error = -ERANGE; } return error; } EXPORT_SYMBOL_GPL(vfs_listxattr); |
5be196e5f [PATCH] add vfs_*... |
276 |
int |
8f0cfa52a xattr: add missin... |
277 |
vfs_removexattr(struct dentry *dentry, const char *name) |
5be196e5f [PATCH] add vfs_*... |
278 279 280 281 282 283 |
{ struct inode *inode = dentry->d_inode; int error; if (!inode->i_op->removexattr) return -EOPNOTSUPP; |
e0ad7b073 [PATCH] move xatt... |
284 285 286 |
error = xattr_permission(inode, name, MAY_WRITE); if (error) return error; |
2ab51f372 vfs: extend vfs_r... |
287 |
mutex_lock(&inode->i_mutex); |
5be196e5f [PATCH] add vfs_*... |
288 |
error = security_inode_removexattr(dentry, name); |
2ab51f372 vfs: extend vfs_r... |
289 290 |
if (error) { mutex_unlock(&inode->i_mutex); |
5be196e5f [PATCH] add vfs_*... |
291 |
return error; |
2ab51f372 vfs: extend vfs_r... |
292 |
} |
5be196e5f [PATCH] add vfs_*... |
293 |
|
5be196e5f [PATCH] add vfs_*... |
294 295 |
error = inode->i_op->removexattr(dentry, name); mutex_unlock(&inode->i_mutex); |
c7b87de23 evm: evm_inode_po... |
296 |
if (!error) { |
5be196e5f [PATCH] add vfs_*... |
297 |
fsnotify_xattr(dentry); |
c7b87de23 evm: evm_inode_po... |
298 299 |
evm_inode_post_removexattr(dentry, name); } |
5be196e5f [PATCH] add vfs_*... |
300 301 302 |
return error; } EXPORT_SYMBOL_GPL(vfs_removexattr); |
1da177e4c Linux-2.6.12-rc2 |
303 304 305 306 |
/* * Extended attribute SET operations */ static long |
8f0cfa52a xattr: add missin... |
307 |
setxattr(struct dentry *d, const char __user *name, const void __user *value, |
1da177e4c Linux-2.6.12-rc2 |
308 309 310 311 |
size_t size, int flags) { int error; void *kvalue = NULL; |
44c824982 fs/xattr.c:setxat... |
312 |
void *vvalue = NULL; /* If non-NULL, we used vmalloc() */ |
1da177e4c Linux-2.6.12-rc2 |
313 314 315 316 317 318 319 320 321 322 323 324 325 326 |
char kname[XATTR_NAME_MAX + 1]; if (flags & ~(XATTR_CREATE|XATTR_REPLACE)) return -EINVAL; error = strncpy_from_user(kname, name, sizeof(kname)); if (error == 0 || error == sizeof(kname)) error = -ERANGE; if (error < 0) return error; if (size) { if (size > XATTR_SIZE_MAX) return -E2BIG; |
44c824982 fs/xattr.c:setxat... |
327 328 329 330 331 332 333 334 335 336 337 |
kvalue = kmalloc(size, GFP_KERNEL | __GFP_NOWARN); if (!kvalue) { vvalue = vmalloc(size); if (!vvalue) return -ENOMEM; kvalue = vvalue; } if (copy_from_user(kvalue, value, size)) { error = -EFAULT; goto out; } |
2f6f0654a userns: Convert v... |
338 339 340 |
if ((strcmp(kname, XATTR_NAME_POSIX_ACL_ACCESS) == 0) || (strcmp(kname, XATTR_NAME_POSIX_ACL_DEFAULT) == 0)) posix_acl_fix_xattr_from_user(kvalue, size); |
1da177e4c Linux-2.6.12-rc2 |
341 |
} |
5be196e5f [PATCH] add vfs_*... |
342 |
error = vfs_setxattr(d, kname, kvalue, size, flags); |
44c824982 fs/xattr.c:setxat... |
343 344 345 346 347 |
out: if (vvalue) vfree(vvalue); else kfree(kvalue); |
1da177e4c Linux-2.6.12-rc2 |
348 349 |
return error; } |
64fd1de3d [CVE-2009-0029] S... |
350 351 352 |
SYSCALL_DEFINE5(setxattr, const char __user *, pathname, const char __user *, name, const void __user *, value, size_t, size, int, flags) |
1da177e4c Linux-2.6.12-rc2 |
353 |
{ |
2d8f30380 [PATCH] sanitize ... |
354 |
struct path path; |
1da177e4c Linux-2.6.12-rc2 |
355 |
int error; |
68f1bb8bb vfs: allow setxat... |
356 357 358 |
unsigned int lookup_flags = LOOKUP_FOLLOW; retry: error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path); |
1da177e4c Linux-2.6.12-rc2 |
359 360 |
if (error) return error; |
2d8f30380 [PATCH] sanitize ... |
361 |
error = mnt_want_write(path.mnt); |
18f335aff [PATCH] r/o bind ... |
362 |
if (!error) { |
2d8f30380 [PATCH] sanitize ... |
363 364 |
error = setxattr(path.dentry, name, value, size, flags); mnt_drop_write(path.mnt); |
18f335aff [PATCH] r/o bind ... |
365 |
} |
2d8f30380 [PATCH] sanitize ... |
366 |
path_put(&path); |
68f1bb8bb vfs: allow setxat... |
367 368 369 370 |
if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; } |
1da177e4c Linux-2.6.12-rc2 |
371 372 |
return error; } |
64fd1de3d [CVE-2009-0029] S... |
373 374 375 |
SYSCALL_DEFINE5(lsetxattr, const char __user *, pathname, const char __user *, name, const void __user *, value, size_t, size, int, flags) |
1da177e4c Linux-2.6.12-rc2 |
376 |
{ |
2d8f30380 [PATCH] sanitize ... |
377 |
struct path path; |
1da177e4c Linux-2.6.12-rc2 |
378 |
int error; |
49e09e1cc vfs: allow lsetxa... |
379 380 381 |
unsigned int lookup_flags = 0; retry: error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path); |
1da177e4c Linux-2.6.12-rc2 |
382 383 |
if (error) return error; |
2d8f30380 [PATCH] sanitize ... |
384 |
error = mnt_want_write(path.mnt); |
18f335aff [PATCH] r/o bind ... |
385 |
if (!error) { |
2d8f30380 [PATCH] sanitize ... |
386 387 |
error = setxattr(path.dentry, name, value, size, flags); mnt_drop_write(path.mnt); |
18f335aff [PATCH] r/o bind ... |
388 |
} |
2d8f30380 [PATCH] sanitize ... |
389 |
path_put(&path); |
49e09e1cc vfs: allow lsetxa... |
390 391 392 393 |
if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; } |
1da177e4c Linux-2.6.12-rc2 |
394 395 |
return error; } |
64fd1de3d [CVE-2009-0029] S... |
396 397 |
SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name, const void __user *,value, size_t, size, int, flags) |
1da177e4c Linux-2.6.12-rc2 |
398 |
{ |
2903ff019 switch simple cas... |
399 |
struct fd f = fdget(fd); |
73241ccca [PATCH] Collect m... |
400 |
struct dentry *dentry; |
1da177e4c Linux-2.6.12-rc2 |
401 |
int error = -EBADF; |
2903ff019 switch simple cas... |
402 |
if (!f.file) |
1da177e4c Linux-2.6.12-rc2 |
403 |
return error; |
2903ff019 switch simple cas... |
404 |
dentry = f.file->f_path.dentry; |
bfcec7087 audit: set the na... |
405 |
audit_inode(NULL, dentry, 0); |
2903ff019 switch simple cas... |
406 |
error = mnt_want_write_file(f.file); |
18f335aff [PATCH] r/o bind ... |
407 408 |
if (!error) { error = setxattr(dentry, name, value, size, flags); |
2903ff019 switch simple cas... |
409 |
mnt_drop_write_file(f.file); |
18f335aff [PATCH] r/o bind ... |
410 |
} |
2903ff019 switch simple cas... |
411 |
fdput(f); |
1da177e4c Linux-2.6.12-rc2 |
412 413 414 415 416 417 418 |
return error; } /* * Extended attribute GET operations */ static ssize_t |
8f0cfa52a xattr: add missin... |
419 420 |
getxattr(struct dentry *d, const char __user *name, void __user *value, size_t size) |
1da177e4c Linux-2.6.12-rc2 |
421 422 423 |
{ ssize_t error; void *kvalue = NULL; |
779302e67 fs/xattr.c:getxat... |
424 |
void *vvalue = NULL; |
1da177e4c Linux-2.6.12-rc2 |
425 426 427 428 429 430 431 432 433 434 435 |
char kname[XATTR_NAME_MAX + 1]; error = strncpy_from_user(kname, name, sizeof(kname)); if (error == 0 || error == sizeof(kname)) error = -ERANGE; if (error < 0) return error; if (size) { if (size > XATTR_SIZE_MAX) size = XATTR_SIZE_MAX; |
779302e67 fs/xattr.c:getxat... |
436 437 438 439 440 441 442 |
kvalue = kzalloc(size, GFP_KERNEL | __GFP_NOWARN); if (!kvalue) { vvalue = vmalloc(size); if (!vvalue) return -ENOMEM; kvalue = vvalue; } |
1da177e4c Linux-2.6.12-rc2 |
443 |
} |
5be196e5f [PATCH] add vfs_*... |
444 |
error = vfs_getxattr(d, kname, kvalue, size); |
f549d6c18 [PATCH] Generic V... |
445 |
if (error > 0) { |
2f6f0654a userns: Convert v... |
446 447 448 |
if ((strcmp(kname, XATTR_NAME_POSIX_ACL_ACCESS) == 0) || (strcmp(kname, XATTR_NAME_POSIX_ACL_DEFAULT) == 0)) posix_acl_fix_xattr_to_user(kvalue, size); |
f549d6c18 [PATCH] Generic V... |
449 450 451 452 453 454 |
if (size && copy_to_user(value, kvalue, error)) error = -EFAULT; } else if (error == -ERANGE && size >= XATTR_SIZE_MAX) { /* The file system tried to returned a value bigger than XATTR_SIZE_MAX bytes. Not possible. */ error = -E2BIG; |
1da177e4c Linux-2.6.12-rc2 |
455 |
} |
779302e67 fs/xattr.c:getxat... |
456 457 458 459 |
if (vvalue) vfree(vvalue); else kfree(kvalue); |
1da177e4c Linux-2.6.12-rc2 |
460 461 |
return error; } |
64fd1de3d [CVE-2009-0029] S... |
462 463 |
SYSCALL_DEFINE4(getxattr, const char __user *, pathname, const char __user *, name, void __user *, value, size_t, size) |
1da177e4c Linux-2.6.12-rc2 |
464 |
{ |
2d8f30380 [PATCH] sanitize ... |
465 |
struct path path; |
1da177e4c Linux-2.6.12-rc2 |
466 |
ssize_t error; |
60e66b48c vfs: make getxatt... |
467 468 469 |
unsigned int lookup_flags = LOOKUP_FOLLOW; retry: error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path); |
1da177e4c Linux-2.6.12-rc2 |
470 471 |
if (error) return error; |
2d8f30380 [PATCH] sanitize ... |
472 473 |
error = getxattr(path.dentry, name, value, size); path_put(&path); |
60e66b48c vfs: make getxatt... |
474 475 476 477 |
if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; } |
1da177e4c Linux-2.6.12-rc2 |
478 479 |
return error; } |
64fd1de3d [CVE-2009-0029] S... |
480 481 |
SYSCALL_DEFINE4(lgetxattr, const char __user *, pathname, const char __user *, name, void __user *, value, size_t, size) |
1da177e4c Linux-2.6.12-rc2 |
482 |
{ |
2d8f30380 [PATCH] sanitize ... |
483 |
struct path path; |
1da177e4c Linux-2.6.12-rc2 |
484 |
ssize_t error; |
3a3e159db vfs: make lgetxat... |
485 486 487 |
unsigned int lookup_flags = 0; retry: error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path); |
1da177e4c Linux-2.6.12-rc2 |
488 489 |
if (error) return error; |
2d8f30380 [PATCH] sanitize ... |
490 491 |
error = getxattr(path.dentry, name, value, size); path_put(&path); |
3a3e159db vfs: make lgetxat... |
492 493 494 495 |
if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; } |
1da177e4c Linux-2.6.12-rc2 |
496 497 |
return error; } |
64fd1de3d [CVE-2009-0029] S... |
498 499 |
SYSCALL_DEFINE4(fgetxattr, int, fd, const char __user *, name, void __user *, value, size_t, size) |
1da177e4c Linux-2.6.12-rc2 |
500 |
{ |
2903ff019 switch simple cas... |
501 |
struct fd f = fdget(fd); |
1da177e4c Linux-2.6.12-rc2 |
502 |
ssize_t error = -EBADF; |
2903ff019 switch simple cas... |
503 |
if (!f.file) |
1da177e4c Linux-2.6.12-rc2 |
504 |
return error; |
bfcec7087 audit: set the na... |
505 |
audit_inode(NULL, f.file->f_path.dentry, 0); |
2903ff019 switch simple cas... |
506 507 |
error = getxattr(f.file->f_path.dentry, name, value, size); fdput(f); |
1da177e4c Linux-2.6.12-rc2 |
508 509 510 511 512 513 514 515 516 517 518 |
return error; } /* * Extended attribute LIST operations */ static ssize_t listxattr(struct dentry *d, char __user *list, size_t size) { ssize_t error; char *klist = NULL; |
0d08d7b7e fs/xattr.c:listxa... |
519 |
char *vlist = NULL; /* If non-NULL, we used vmalloc() */ |
1da177e4c Linux-2.6.12-rc2 |
520 521 522 523 |
if (size) { if (size > XATTR_LIST_MAX) size = XATTR_LIST_MAX; |
703bf2d12 fs/xattr.c: suppr... |
524 |
klist = kmalloc(size, __GFP_NOWARN | GFP_KERNEL); |
0d08d7b7e fs/xattr.c:listxa... |
525 526 527 528 529 530 |
if (!klist) { vlist = vmalloc(size); if (!vlist) return -ENOMEM; klist = vlist; } |
1da177e4c Linux-2.6.12-rc2 |
531 |
} |
659564c8a [PATCH] Introduce... |
532 |
error = vfs_listxattr(d, klist, size); |
f549d6c18 [PATCH] Generic V... |
533 534 535 536 537 538 539 |
if (error > 0) { if (size && copy_to_user(list, klist, error)) error = -EFAULT; } else if (error == -ERANGE && size >= XATTR_LIST_MAX) { /* The file system tried to returned a list bigger than XATTR_LIST_MAX bytes. Not possible. */ error = -E2BIG; |
1da177e4c Linux-2.6.12-rc2 |
540 |
} |
0d08d7b7e fs/xattr.c:listxa... |
541 542 543 544 |
if (vlist) vfree(vlist); else kfree(klist); |
1da177e4c Linux-2.6.12-rc2 |
545 546 |
return error; } |
64fd1de3d [CVE-2009-0029] S... |
547 548 |
SYSCALL_DEFINE3(listxattr, const char __user *, pathname, char __user *, list, size_t, size) |
1da177e4c Linux-2.6.12-rc2 |
549 |
{ |
2d8f30380 [PATCH] sanitize ... |
550 |
struct path path; |
1da177e4c Linux-2.6.12-rc2 |
551 |
ssize_t error; |
10a90cf36 vfs: make listxat... |
552 553 554 |
unsigned int lookup_flags = LOOKUP_FOLLOW; retry: error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path); |
1da177e4c Linux-2.6.12-rc2 |
555 556 |
if (error) return error; |
2d8f30380 [PATCH] sanitize ... |
557 558 |
error = listxattr(path.dentry, list, size); path_put(&path); |
10a90cf36 vfs: make listxat... |
559 560 561 562 |
if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; } |
1da177e4c Linux-2.6.12-rc2 |
563 564 |
return error; } |
64fd1de3d [CVE-2009-0029] S... |
565 566 |
SYSCALL_DEFINE3(llistxattr, const char __user *, pathname, char __user *, list, size_t, size) |
1da177e4c Linux-2.6.12-rc2 |
567 |
{ |
2d8f30380 [PATCH] sanitize ... |
568 |
struct path path; |
1da177e4c Linux-2.6.12-rc2 |
569 |
ssize_t error; |
bd9bbc984 vfs: make llistxa... |
570 571 572 |
unsigned int lookup_flags = 0; retry: error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path); |
1da177e4c Linux-2.6.12-rc2 |
573 574 |
if (error) return error; |
2d8f30380 [PATCH] sanitize ... |
575 576 |
error = listxattr(path.dentry, list, size); path_put(&path); |
bd9bbc984 vfs: make llistxa... |
577 578 579 580 |
if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; } |
1da177e4c Linux-2.6.12-rc2 |
581 582 |
return error; } |
64fd1de3d [CVE-2009-0029] S... |
583 |
SYSCALL_DEFINE3(flistxattr, int, fd, char __user *, list, size_t, size) |
1da177e4c Linux-2.6.12-rc2 |
584 |
{ |
2903ff019 switch simple cas... |
585 |
struct fd f = fdget(fd); |
1da177e4c Linux-2.6.12-rc2 |
586 |
ssize_t error = -EBADF; |
2903ff019 switch simple cas... |
587 |
if (!f.file) |
1da177e4c Linux-2.6.12-rc2 |
588 |
return error; |
bfcec7087 audit: set the na... |
589 |
audit_inode(NULL, f.file->f_path.dentry, 0); |
2903ff019 switch simple cas... |
590 591 |
error = listxattr(f.file->f_path.dentry, list, size); fdput(f); |
1da177e4c Linux-2.6.12-rc2 |
592 593 594 595 596 597 598 |
return error; } /* * Extended attribute REMOVE operations */ static long |
8f0cfa52a xattr: add missin... |
599 |
removexattr(struct dentry *d, const char __user *name) |
1da177e4c Linux-2.6.12-rc2 |
600 601 602 603 604 605 606 607 608 |
{ int error; char kname[XATTR_NAME_MAX + 1]; error = strncpy_from_user(kname, name, sizeof(kname)); if (error == 0 || error == sizeof(kname)) error = -ERANGE; if (error < 0) return error; |
5be196e5f [PATCH] add vfs_*... |
609 |
return vfs_removexattr(d, kname); |
1da177e4c Linux-2.6.12-rc2 |
610 |
} |
64fd1de3d [CVE-2009-0029] S... |
611 612 |
SYSCALL_DEFINE2(removexattr, const char __user *, pathname, const char __user *, name) |
1da177e4c Linux-2.6.12-rc2 |
613 |
{ |
2d8f30380 [PATCH] sanitize ... |
614 |
struct path path; |
1da177e4c Linux-2.6.12-rc2 |
615 |
int error; |
12f062129 vfs: make removex... |
616 617 618 |
unsigned int lookup_flags = LOOKUP_FOLLOW; retry: error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path); |
1da177e4c Linux-2.6.12-rc2 |
619 620 |
if (error) return error; |
2d8f30380 [PATCH] sanitize ... |
621 |
error = mnt_want_write(path.mnt); |
18f335aff [PATCH] r/o bind ... |
622 |
if (!error) { |
2d8f30380 [PATCH] sanitize ... |
623 624 |
error = removexattr(path.dentry, name); mnt_drop_write(path.mnt); |
18f335aff [PATCH] r/o bind ... |
625 |
} |
2d8f30380 [PATCH] sanitize ... |
626 |
path_put(&path); |
12f062129 vfs: make removex... |
627 628 629 630 |
if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; } |
1da177e4c Linux-2.6.12-rc2 |
631 632 |
return error; } |
6a6160a7b [CVE-2009-0029] S... |
633 634 |
SYSCALL_DEFINE2(lremovexattr, const char __user *, pathname, const char __user *, name) |
1da177e4c Linux-2.6.12-rc2 |
635 |
{ |
2d8f30380 [PATCH] sanitize ... |
636 |
struct path path; |
1da177e4c Linux-2.6.12-rc2 |
637 |
int error; |
b729d75d1 vfs: make lremove... |
638 639 640 |
unsigned int lookup_flags = 0; retry: error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path); |
1da177e4c Linux-2.6.12-rc2 |
641 642 |
if (error) return error; |
2d8f30380 [PATCH] sanitize ... |
643 |
error = mnt_want_write(path.mnt); |
18f335aff [PATCH] r/o bind ... |
644 |
if (!error) { |
2d8f30380 [PATCH] sanitize ... |
645 646 |
error = removexattr(path.dentry, name); mnt_drop_write(path.mnt); |
18f335aff [PATCH] r/o bind ... |
647 |
} |
2d8f30380 [PATCH] sanitize ... |
648 |
path_put(&path); |
b729d75d1 vfs: make lremove... |
649 650 651 652 |
if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; } |
1da177e4c Linux-2.6.12-rc2 |
653 654 |
return error; } |
6a6160a7b [CVE-2009-0029] S... |
655 |
SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name) |
1da177e4c Linux-2.6.12-rc2 |
656 |
{ |
2903ff019 switch simple cas... |
657 |
struct fd f = fdget(fd); |
73241ccca [PATCH] Collect m... |
658 |
struct dentry *dentry; |
1da177e4c Linux-2.6.12-rc2 |
659 |
int error = -EBADF; |
2903ff019 switch simple cas... |
660 |
if (!f.file) |
1da177e4c Linux-2.6.12-rc2 |
661 |
return error; |
2903ff019 switch simple cas... |
662 |
dentry = f.file->f_path.dentry; |
bfcec7087 audit: set the na... |
663 |
audit_inode(NULL, dentry, 0); |
2903ff019 switch simple cas... |
664 |
error = mnt_want_write_file(f.file); |
18f335aff [PATCH] r/o bind ... |
665 666 |
if (!error) { error = removexattr(dentry, name); |
2903ff019 switch simple cas... |
667 |
mnt_drop_write_file(f.file); |
18f335aff [PATCH] r/o bind ... |
668 |
} |
2903ff019 switch simple cas... |
669 |
fdput(f); |
1da177e4c Linux-2.6.12-rc2 |
670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 |
return error; } static const char * strcmp_prefix(const char *a, const char *a_prefix) { while (*a_prefix && *a == *a_prefix) { a++; a_prefix++; } return *a_prefix ? NULL : a; } /* * In order to implement different sets of xattr operations for each xattr * prefix with the generic xattr API, a filesystem should create a * null-terminated array of struct xattr_handler (one for each prefix) and * hang a pointer to it off of the s_xattr field of the superblock. * * The generic_fooxattr() functions will use this list to dispatch xattr * operations to the correct xattr_handler. */ #define for_each_xattr_handler(handlers, handler) \ for ((handler) = *(handlers)++; \ (handler) != NULL; \ (handler) = *(handlers)++) /* * Find the xattr_handler with the matching prefix. */ |
bb4354538 fs: xattr_handler... |
701 702 |
static const struct xattr_handler * xattr_resolve_name(const struct xattr_handler **handlers, const char **name) |
1da177e4c Linux-2.6.12-rc2 |
703 |
{ |
bb4354538 fs: xattr_handler... |
704 |
const struct xattr_handler *handler; |
1da177e4c Linux-2.6.12-rc2 |
705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 |
if (!*name) return NULL; for_each_xattr_handler(handlers, handler) { const char *n = strcmp_prefix(*name, handler->prefix); if (n) { *name = n; break; } } return handler; } /* * Find the handler for the prefix and dispatch its get() operation. */ ssize_t generic_getxattr(struct dentry *dentry, const char *name, void *buffer, size_t size) { |
bb4354538 fs: xattr_handler... |
725 |
const struct xattr_handler *handler; |
1da177e4c Linux-2.6.12-rc2 |
726 |
|
431547b3c sanitize xattr ha... |
727 |
handler = xattr_resolve_name(dentry->d_sb->s_xattr, &name); |
1da177e4c Linux-2.6.12-rc2 |
728 729 |
if (!handler) return -EOPNOTSUPP; |
431547b3c sanitize xattr ha... |
730 |
return handler->get(dentry, name, buffer, size, handler->flags); |
1da177e4c Linux-2.6.12-rc2 |
731 732 733 734 735 736 737 738 739 |
} /* * Combine the results of the list() operation from every xattr_handler in the * list. */ ssize_t generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) { |
bb4354538 fs: xattr_handler... |
740 |
const struct xattr_handler *handler, **handlers = dentry->d_sb->s_xattr; |
1da177e4c Linux-2.6.12-rc2 |
741 742 743 |
unsigned int size = 0; if (!buffer) { |
431547b3c sanitize xattr ha... |
744 745 746 747 |
for_each_xattr_handler(handlers, handler) { size += handler->list(dentry, NULL, 0, NULL, 0, handler->flags); } |
1da177e4c Linux-2.6.12-rc2 |
748 749 750 751 |
} else { char *buf = buffer; for_each_xattr_handler(handlers, handler) { |
431547b3c sanitize xattr ha... |
752 753 |
size = handler->list(dentry, buf, buffer_size, NULL, 0, handler->flags); |
1da177e4c Linux-2.6.12-rc2 |
754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 |
if (size > buffer_size) return -ERANGE; buf += size; buffer_size -= size; } size = buf - buffer; } return size; } /* * Find the handler for the prefix and dispatch its set() operation. */ int generic_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { |
bb4354538 fs: xattr_handler... |
770 |
const struct xattr_handler *handler; |
1da177e4c Linux-2.6.12-rc2 |
771 772 773 |
if (size == 0) value = ""; /* empty EA, do not remove */ |
431547b3c sanitize xattr ha... |
774 |
handler = xattr_resolve_name(dentry->d_sb->s_xattr, &name); |
1da177e4c Linux-2.6.12-rc2 |
775 776 |
if (!handler) return -EOPNOTSUPP; |
df7e13038 vfs: Pass setxatt... |
777 |
return handler->set(dentry, name, value, size, flags, handler->flags); |
1da177e4c Linux-2.6.12-rc2 |
778 779 780 781 782 783 784 785 786 |
} /* * Find the handler for the prefix and dispatch its set() operation to remove * any associated extended attribute. */ int generic_removexattr(struct dentry *dentry, const char *name) { |
bb4354538 fs: xattr_handler... |
787 |
const struct xattr_handler *handler; |
1da177e4c Linux-2.6.12-rc2 |
788 |
|
431547b3c sanitize xattr ha... |
789 |
handler = xattr_resolve_name(dentry->d_sb->s_xattr, &name); |
1da177e4c Linux-2.6.12-rc2 |
790 791 |
if (!handler) return -EOPNOTSUPP; |
431547b3c sanitize xattr ha... |
792 793 |
return handler->set(dentry, name, NULL, 0, XATTR_REPLACE, handler->flags); |
1da177e4c Linux-2.6.12-rc2 |
794 795 796 797 798 799 |
} EXPORT_SYMBOL(generic_getxattr); EXPORT_SYMBOL(generic_listxattr); EXPORT_SYMBOL(generic_setxattr); EXPORT_SYMBOL(generic_removexattr); |
38f386574 xattr: extract si... |
800 801 802 803 804 805 806 807 808 809 810 |
/* * Allocate new xattr and copy in the value; but leave the name to callers. */ struct simple_xattr *simple_xattr_alloc(const void *value, size_t size) { struct simple_xattr *new_xattr; size_t len; /* wrap around? */ len = sizeof(*new_xattr) + size; |
4e66d445d simple_xattr: per... |
811 |
if (len < sizeof(*new_xattr)) |
38f386574 xattr: extract si... |
812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 |
return NULL; new_xattr = kmalloc(len, GFP_KERNEL); if (!new_xattr) return NULL; new_xattr->size = size; memcpy(new_xattr->value, value, size); return new_xattr; } /* * xattr GET operation for in-memory/pseudo filesystems */ int simple_xattr_get(struct simple_xattrs *xattrs, const char *name, void *buffer, size_t size) { struct simple_xattr *xattr; int ret = -ENODATA; spin_lock(&xattrs->lock); list_for_each_entry(xattr, &xattrs->head, list) { if (strcmp(name, xattr->name)) continue; ret = xattr->size; if (buffer) { if (size < xattr->size) ret = -ERANGE; else memcpy(buffer, xattr->value, xattr->size); } break; } spin_unlock(&xattrs->lock); return ret; } static int __simple_xattr_set(struct simple_xattrs *xattrs, const char *name, const void *value, size_t size, int flags) { struct simple_xattr *xattr; |
433858469 fs, xattr: fix bu... |
854 |
struct simple_xattr *new_xattr = NULL; |
38f386574 xattr: extract si... |
855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 |
int err = 0; /* value == NULL means remove */ if (value) { new_xattr = simple_xattr_alloc(value, size); if (!new_xattr) return -ENOMEM; new_xattr->name = kstrdup(name, GFP_KERNEL); if (!new_xattr->name) { kfree(new_xattr); return -ENOMEM; } } spin_lock(&xattrs->lock); list_for_each_entry(xattr, &xattrs->head, list) { if (!strcmp(name, xattr->name)) { if (flags & XATTR_CREATE) { xattr = new_xattr; err = -EEXIST; } else if (new_xattr) { list_replace(&xattr->list, &new_xattr->list); } else { list_del(&xattr->list); } goto out; } } if (flags & XATTR_REPLACE) { xattr = new_xattr; err = -ENODATA; } else { list_add(&new_xattr->list, &xattrs->head); xattr = NULL; } out: spin_unlock(&xattrs->lock); if (xattr) { kfree(xattr->name); kfree(xattr); } return err; } |
4895768b6 fs: add missing d... |
900 901 902 903 904 905 906 907 908 909 910 911 912 |
/** * simple_xattr_set - xattr SET operation for in-memory/pseudo filesystems * @xattrs: target simple_xattr list * @name: name of the new extended attribute * @value: value of the new xattr. If %NULL, will remove the attribute * @size: size of the new xattr * @flags: %XATTR_{CREATE|REPLACE} * * %XATTR_CREATE is set, the xattr shouldn't exist already; otherwise fails * with -EEXIST. If %XATTR_REPLACE is set, the xattr should exist; * otherwise, fails with -ENODATA. * * Returns 0 on success, -errno on failure. |
38f386574 xattr: extract si... |
913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 |
*/ int simple_xattr_set(struct simple_xattrs *xattrs, const char *name, const void *value, size_t size, int flags) { if (size == 0) value = ""; /* empty EA, do not remove */ return __simple_xattr_set(xattrs, name, value, size, flags); } /* * xattr REMOVE operation for in-memory/pseudo filesystems */ int simple_xattr_remove(struct simple_xattrs *xattrs, const char *name) { return __simple_xattr_set(xattrs, name, NULL, 0, XATTR_REPLACE); } static bool xattr_is_trusted(const char *name) { return !strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN); } /* * xattr LIST operation for in-memory/pseudo filesystems */ ssize_t simple_xattr_list(struct simple_xattrs *xattrs, char *buffer, size_t size) { bool trusted = capable(CAP_SYS_ADMIN); struct simple_xattr *xattr; size_t used = 0; spin_lock(&xattrs->lock); list_for_each_entry(xattr, &xattrs->head, list) { size_t len; /* skip "trusted." attributes for unprivileged callers */ if (!trusted && xattr_is_trusted(xattr->name)) continue; len = strlen(xattr->name) + 1; used += len; if (buffer) { if (size < used) { used = -ERANGE; break; } memcpy(buffer, xattr->name, len); buffer += len; } } spin_unlock(&xattrs->lock); return used; } |
4895768b6 fs: add missing d... |
968 969 970 |
/* * Adds an extended attribute to the list */ |
38f386574 xattr: extract si... |
971 972 973 974 975 976 977 |
void simple_xattr_list_add(struct simple_xattrs *xattrs, struct simple_xattr *new_xattr) { spin_lock(&xattrs->lock); list_add(&new_xattr->list, &xattrs->head); spin_unlock(&xattrs->lock); } |