linux-curproc.c revision 9a28b1881cef46727d7ba81a5d743db2321155b0
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 58void cfs_cap_raise(cfs_cap_t cap) 59{ 60 struct cred *cred; 61 if ((cred = prepare_creds())) { 62 cap_raise(cred->cap_effective, cap); 63 commit_creds(cred); 64 } 65} 66 67void cfs_cap_lower(cfs_cap_t cap) 68{ 69 struct cred *cred; 70 if ((cred = prepare_creds())) { 71 cap_lower(cred->cap_effective, cap); 72 commit_creds(cred); 73 } 74} 75 76int cfs_cap_raised(cfs_cap_t cap) 77{ 78 return cap_raised(current_cap(), cap); 79} 80 81void cfs_kernel_cap_pack(kernel_cap_t kcap, cfs_cap_t *cap) 82{ 83#if defined (_LINUX_CAPABILITY_VERSION) && _LINUX_CAPABILITY_VERSION == 0x19980330 84 *cap = kcap; 85#elif defined (_LINUX_CAPABILITY_VERSION) && _LINUX_CAPABILITY_VERSION == 0x20071026 86 *cap = kcap[0]; 87#elif defined(_KERNEL_CAPABILITY_VERSION) && _KERNEL_CAPABILITY_VERSION == 0x20080522 88 /* XXX lost high byte */ 89 *cap = kcap.cap[0]; 90#else 91 #error "need correct _KERNEL_CAPABILITY_VERSION " 92#endif 93} 94 95void cfs_kernel_cap_unpack(kernel_cap_t *kcap, cfs_cap_t cap) 96{ 97#if defined (_LINUX_CAPABILITY_VERSION) && _LINUX_CAPABILITY_VERSION == 0x19980330 98 *kcap = cap; 99#elif defined (_LINUX_CAPABILITY_VERSION) && _LINUX_CAPABILITY_VERSION == 0x20071026 100 (*kcap)[0] = cap; 101#elif defined(_KERNEL_CAPABILITY_VERSION) && _KERNEL_CAPABILITY_VERSION == 0x20080522 102 kcap->cap[0] = cap; 103#else 104 #error "need correct _KERNEL_CAPABILITY_VERSION " 105#endif 106} 107 108cfs_cap_t cfs_curproc_cap_pack(void) 109{ 110 cfs_cap_t cap; 111 cfs_kernel_cap_pack(current_cap(), &cap); 112 return cap; 113} 114 115int cfs_capable(cfs_cap_t cap) 116{ 117 return capable(cap); 118} 119 120static int cfs_access_process_vm(struct task_struct *tsk, unsigned long addr, 121 void *buf, int len, int write) 122{ 123 /* Just copied from kernel for the kernels which doesn't 124 * have access_process_vm() exported */ 125 struct mm_struct *mm; 126 struct vm_area_struct *vma; 127 struct page *page; 128 void *old_buf = buf; 129 130 mm = get_task_mm(tsk); 131 if (!mm) 132 return 0; 133 134 down_read(&mm->mmap_sem); 135 /* ignore errors, just check how much was successfully transferred */ 136 while (len) { 137 int bytes, rc, offset; 138 void *maddr; 139 140 rc = get_user_pages(tsk, mm, addr, 1, 141 write, 1, &page, &vma); 142 if (rc <= 0) 143 break; 144 145 bytes = len; 146 offset = addr & (PAGE_SIZE-1); 147 if (bytes > PAGE_SIZE-offset) 148 bytes = PAGE_SIZE-offset; 149 150 maddr = kmap(page); 151 if (write) { 152 copy_to_user_page(vma, page, addr, 153 maddr + offset, buf, bytes); 154 set_page_dirty_lock(page); 155 } else { 156 copy_from_user_page(vma, page, addr, 157 buf, maddr + offset, bytes); 158 } 159 kunmap(page); 160 page_cache_release(page); 161 len -= bytes; 162 buf += bytes; 163 addr += bytes; 164 } 165 up_read(&mm->mmap_sem); 166 mmput(mm); 167 168 return buf - old_buf; 169} 170 171/* Read the environment variable of current process specified by @key. */ 172int cfs_get_environ(const char *key, char *value, int *val_len) 173{ 174 struct mm_struct *mm; 175 char *buffer, *tmp_buf = NULL; 176 int buf_len = PAGE_CACHE_SIZE; 177 int key_len = strlen(key); 178 unsigned long addr; 179 int rc; 180 181 buffer = kmalloc(buf_len, GFP_USER); 182 if (!buffer) 183 return -ENOMEM; 184 185 mm = get_task_mm(current); 186 if (!mm) { 187 kfree(buffer); 188 return -EINVAL; 189 } 190 191 /* Avoid deadlocks on mmap_sem if called from sys_mmap_pgoff(), 192 * which is already holding mmap_sem for writes. If some other 193 * thread gets the write lock in the meantime, this thread will 194 * block, but at least it won't deadlock on itself. LU-1735 */ 195 if (down_read_trylock(&mm->mmap_sem) == 0) { 196 kfree(buffer); 197 return -EDEADLK; 198 } 199 up_read(&mm->mmap_sem); 200 201 addr = mm->env_start; 202 while (addr < mm->env_end) { 203 int this_len, retval, scan_len; 204 char *env_start, *env_end; 205 206 memset(buffer, 0, buf_len); 207 208 this_len = min_t(int, mm->env_end - addr, buf_len); 209 retval = cfs_access_process_vm(current, addr, buffer, 210 this_len, 0); 211 if (retval != this_len) 212 break; 213 214 addr += retval; 215 216 /* Parse the buffer to find out the specified key/value pair. 217 * The "key=value" entries are separated by '\0'. */ 218 env_start = buffer; 219 scan_len = this_len; 220 while (scan_len) { 221 char *entry; 222 int entry_len; 223 224 env_end = memscan(env_start, '\0', scan_len); 225 LASSERT(env_end >= env_start && 226 env_end <= env_start + scan_len); 227 228 /* The last entry of this buffer cross the buffer 229 * boundary, reread it in next cycle. */ 230 if (unlikely(env_end - env_start == scan_len)) { 231 /* This entry is too large to fit in buffer */ 232 if (unlikely(scan_len == this_len)) { 233 CERROR("Too long env variable.\n"); 234 GOTO(out, rc = -EINVAL); 235 } 236 addr -= scan_len; 237 break; 238 } 239 240 entry = env_start; 241 entry_len = env_end - env_start; 242 243 /* Key length + length of '=' */ 244 if (entry_len > key_len + 1 && 245 !memcmp(entry, key, key_len)) { 246 entry += key_len + 1; 247 entry_len -= key_len + 1; 248 /* The 'value' buffer passed in is too small.*/ 249 if (entry_len >= *val_len) 250 GOTO(out, rc = -EOVERFLOW); 251 252 memcpy(value, entry, entry_len); 253 *val_len = entry_len; 254 GOTO(out, rc = 0); 255 } 256 257 scan_len -= (env_end - env_start + 1); 258 env_start = env_end + 1; 259 } 260 } 261 GOTO(out, rc = -ENOENT); 262 263out: 264 mmput(mm); 265 kfree((void *)buffer); 266 if (tmp_buf) 267 kfree((void *)tmp_buf); 268 return rc; 269} 270EXPORT_SYMBOL(cfs_get_environ); 271 272EXPORT_SYMBOL(cfs_cap_raise); 273EXPORT_SYMBOL(cfs_cap_lower); 274EXPORT_SYMBOL(cfs_cap_raised); 275EXPORT_SYMBOL(cfs_curproc_cap_pack); 276EXPORT_SYMBOL(cfs_capable); 277 278/* 279 * Local variables: 280 * c-indentation-style: "K&R" 281 * c-basic-offset: 8 282 * tab-width: 8 283 * fill-column: 80 284 * scroll-step: 1 285 * End: 286 */ 287