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