linux-curproc.c revision 82c3fef5682d501d02ccb8b32dcc24ecad7986e6
1/* 2 * GPL HEADER START 3 * 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 only, 8 * as published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, but 11 * WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * General Public License version 2 for more details (a copy is included 14 * in the LICENSE file that accompanied this code). 15 * 16 * You should have received a copy of the GNU General Public License 17 * version 2 along with this program; If not, see 18 * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf 19 * 20 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 21 * CA 95054 USA or visit www.sun.com if you need additional information or 22 * have any questions. 23 * 24 * GPL HEADER END 25 */ 26/* 27 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 28 * Use is subject to license terms. 29 * 30 * Copyright (c) 2011, 2012, Intel Corporation. 31 */ 32/* 33 * This file is part of Lustre, http://www.lustre.org/ 34 * Lustre is a trademark of Sun Microsystems, Inc. 35 * 36 * libcfs/libcfs/linux/linux-curproc.c 37 * 38 * Lustre curproc API implementation for Linux kernel 39 * 40 * Author: Nikita Danilov <nikita@clusterfs.com> 41 */ 42 43#include <linux/sched.h> 44#include <linux/fs_struct.h> 45 46#include <linux/compat.h> 47#include <linux/thread_info.h> 48 49#define DEBUG_SUBSYSTEM S_LNET 50 51#include <linux/libcfs/libcfs.h> 52 53/* 54 * Implementation of cfs_curproc API (see portals/include/libcfs/curproc.h) 55 * for Linux kernel. 56 */ 57 58/* Currently all the CFS_CAP_* defines match CAP_* ones. */ 59#define cfs_cap_pack(cap) (cap) 60#define cfs_cap_unpack(cap) (cap) 61 62void cfs_cap_raise(cfs_cap_t cap) 63{ 64 struct cred *cred; 65 if ((cred = prepare_creds())) { 66 cap_raise(cred->cap_effective, cfs_cap_unpack(cap)); 67 commit_creds(cred); 68 } 69} 70 71void cfs_cap_lower(cfs_cap_t cap) 72{ 73 struct cred *cred; 74 if ((cred = prepare_creds())) { 75 cap_lower(cred->cap_effective, cfs_cap_unpack(cap)); 76 commit_creds(cred); 77 } 78} 79 80int cfs_cap_raised(cfs_cap_t cap) 81{ 82 return cap_raised(current_cap(), cfs_cap_unpack(cap)); 83} 84 85void cfs_kernel_cap_pack(kernel_cap_t kcap, cfs_cap_t *cap) 86{ 87#if defined (_LINUX_CAPABILITY_VERSION) && _LINUX_CAPABILITY_VERSION == 0x19980330 88 *cap = cfs_cap_pack(kcap); 89#elif defined (_LINUX_CAPABILITY_VERSION) && _LINUX_CAPABILITY_VERSION == 0x20071026 90 *cap = cfs_cap_pack(kcap[0]); 91#elif defined(_KERNEL_CAPABILITY_VERSION) && _KERNEL_CAPABILITY_VERSION == 0x20080522 92 /* XXX lost high byte */ 93 *cap = cfs_cap_pack(kcap.cap[0]); 94#else 95 #error "need correct _KERNEL_CAPABILITY_VERSION " 96#endif 97} 98 99void cfs_kernel_cap_unpack(kernel_cap_t *kcap, cfs_cap_t cap) 100{ 101#if defined (_LINUX_CAPABILITY_VERSION) && _LINUX_CAPABILITY_VERSION == 0x19980330 102 *kcap = cfs_cap_unpack(cap); 103#elif defined (_LINUX_CAPABILITY_VERSION) && _LINUX_CAPABILITY_VERSION == 0x20071026 104 (*kcap)[0] = cfs_cap_unpack(cap); 105#elif defined(_KERNEL_CAPABILITY_VERSION) && _KERNEL_CAPABILITY_VERSION == 0x20080522 106 kcap->cap[0] = cfs_cap_unpack(cap); 107#else 108 #error "need correct _KERNEL_CAPABILITY_VERSION " 109#endif 110} 111 112cfs_cap_t cfs_curproc_cap_pack(void) 113{ 114 cfs_cap_t cap; 115 cfs_kernel_cap_pack(current_cap(), &cap); 116 return cap; 117} 118 119void cfs_curproc_cap_unpack(cfs_cap_t cap) 120{ 121 struct cred *cred; 122 if ((cred = prepare_creds())) { 123 cfs_kernel_cap_unpack(&cred->cap_effective, cap); 124 commit_creds(cred); 125 } 126} 127 128int cfs_capable(cfs_cap_t cap) 129{ 130 return capable(cfs_cap_unpack(cap)); 131} 132 133static int cfs_access_process_vm(struct task_struct *tsk, unsigned long addr, 134 void *buf, int len, int write) 135{ 136 /* Just copied from kernel for the kernels which doesn't 137 * have access_process_vm() exported */ 138 struct mm_struct *mm; 139 struct vm_area_struct *vma; 140 struct page *page; 141 void *old_buf = buf; 142 143 mm = get_task_mm(tsk); 144 if (!mm) 145 return 0; 146 147 down_read(&mm->mmap_sem); 148 /* ignore errors, just check how much was successfully transferred */ 149 while (len) { 150 int bytes, rc, offset; 151 void *maddr; 152 153 rc = get_user_pages(tsk, mm, addr, 1, 154 write, 1, &page, &vma); 155 if (rc <= 0) 156 break; 157 158 bytes = len; 159 offset = addr & (PAGE_SIZE-1); 160 if (bytes > PAGE_SIZE-offset) 161 bytes = PAGE_SIZE-offset; 162 163 maddr = kmap(page); 164 if (write) { 165 copy_to_user_page(vma, page, addr, 166 maddr + offset, buf, bytes); 167 set_page_dirty_lock(page); 168 } else { 169 copy_from_user_page(vma, page, addr, 170 buf, maddr + offset, bytes); 171 } 172 kunmap(page); 173 page_cache_release(page); 174 len -= bytes; 175 buf += bytes; 176 addr += bytes; 177 } 178 up_read(&mm->mmap_sem); 179 mmput(mm); 180 181 return buf - old_buf; 182} 183 184/* Read the environment variable of current process specified by @key. */ 185int cfs_get_environ(const char *key, char *value, int *val_len) 186{ 187 struct mm_struct *mm; 188 char *buffer, *tmp_buf = NULL; 189 int buf_len = PAGE_CACHE_SIZE; 190 int key_len = strlen(key); 191 unsigned long addr; 192 int rc; 193 194 buffer = kmalloc(buf_len, GFP_USER); 195 if (!buffer) 196 return -ENOMEM; 197 198 mm = get_task_mm(current); 199 if (!mm) { 200 kfree(buffer); 201 return -EINVAL; 202 } 203 204 /* Avoid deadlocks on mmap_sem if called from sys_mmap_pgoff(), 205 * which is already holding mmap_sem for writes. If some other 206 * thread gets the write lock in the meantime, this thread will 207 * block, but at least it won't deadlock on itself. LU-1735 */ 208 if (down_read_trylock(&mm->mmap_sem) == 0) { 209 kfree(buffer); 210 return -EDEADLK; 211 } 212 up_read(&mm->mmap_sem); 213 214 addr = mm->env_start; 215 while (addr < mm->env_end) { 216 int this_len, retval, scan_len; 217 char *env_start, *env_end; 218 219 memset(buffer, 0, buf_len); 220 221 this_len = min_t(int, mm->env_end - addr, buf_len); 222 retval = cfs_access_process_vm(current, addr, buffer, 223 this_len, 0); 224 if (retval != this_len) 225 break; 226 227 addr += retval; 228 229 /* Parse the buffer to find out the specified key/value pair. 230 * The "key=value" entries are separated by '\0'. */ 231 env_start = buffer; 232 scan_len = this_len; 233 while (scan_len) { 234 char *entry; 235 int entry_len; 236 237 env_end = memscan(env_start, '\0', scan_len); 238 LASSERT(env_end >= env_start && 239 env_end <= env_start + scan_len); 240 241 /* The last entry of this buffer cross the buffer 242 * boundary, reread it in next cycle. */ 243 if (unlikely(env_end - env_start == scan_len)) { 244 /* This entry is too large to fit in buffer */ 245 if (unlikely(scan_len == this_len)) { 246 CERROR("Too long env variable.\n"); 247 GOTO(out, rc = -EINVAL); 248 } 249 addr -= scan_len; 250 break; 251 } 252 253 entry = env_start; 254 entry_len = env_end - env_start; 255 256 /* Key length + length of '=' */ 257 if (entry_len > key_len + 1 && 258 !memcmp(entry, key, key_len)) { 259 entry += key_len + 1; 260 entry_len -= key_len + 1; 261 /* The 'value' buffer passed in is too small.*/ 262 if (entry_len >= *val_len) 263 GOTO(out, rc = -EOVERFLOW); 264 265 memcpy(value, entry, entry_len); 266 *val_len = entry_len; 267 GOTO(out, rc = 0); 268 } 269 270 scan_len -= (env_end - env_start + 1); 271 env_start = env_end + 1; 272 } 273 } 274 GOTO(out, rc = -ENOENT); 275 276out: 277 mmput(mm); 278 kfree((void *)buffer); 279 if (tmp_buf) 280 kfree((void *)tmp_buf); 281 return rc; 282} 283EXPORT_SYMBOL(cfs_get_environ); 284 285EXPORT_SYMBOL(cfs_cap_raise); 286EXPORT_SYMBOL(cfs_cap_lower); 287EXPORT_SYMBOL(cfs_cap_raised); 288EXPORT_SYMBOL(cfs_curproc_cap_pack); 289EXPORT_SYMBOL(cfs_curproc_cap_unpack); 290EXPORT_SYMBOL(cfs_capable); 291 292/* 293 * Local variables: 294 * c-indentation-style: "K&R" 295 * c-basic-offset: 8 296 * tab-width: 8 297 * fill-column: 80 298 * scroll-step: 1 299 * End: 300 */ 301