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