]> git.saurik.com Git - apple/xnu.git/blob - bsd/vm/vm_unix.c
xnu-792.21.3.tar.gz
[apple/xnu.git] / bsd / vm / vm_unix.c
1 /*
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28 /*
29 * Mach Operating System
30 * Copyright (c) 1987 Carnegie-Mellon University
31 * All rights reserved. The CMU software License Agreement specifies
32 * the terms and conditions for use and redistribution.
33 */
34
35 /*
36 */
37
38
39 #include <meta_features.h>
40
41 #include <kern/task.h>
42 #include <kern/thread.h>
43 #include <kern/debug.h>
44 #include <kern/lock.h>
45 #include <mach/mach_traps.h>
46 #include <mach/time_value.h>
47 #include <mach/vm_map.h>
48 #include <mach/vm_param.h>
49 #include <mach/vm_prot.h>
50 #include <mach/port.h>
51
52 #include <sys/file_internal.h>
53 #include <sys/param.h>
54 #include <sys/systm.h>
55 #include <sys/dir.h>
56 #include <sys/namei.h>
57 #include <sys/proc_internal.h>
58 #include <sys/kauth.h>
59 #include <sys/vm.h>
60 #include <sys/file.h>
61 #include <sys/vnode_internal.h>
62 #include <sys/mount.h>
63 #include <sys/trace.h>
64 #include <sys/kernel.h>
65 #include <sys/ubc_internal.h>
66 #include <sys/user.h>
67 #include <sys/stat.h>
68 #include <sys/sysproto.h>
69 #include <sys/mman.h>
70
71 #include <bsm/audit_kernel.h>
72 #include <bsm/audit_kevents.h>
73
74 #include <kern/kalloc.h>
75 #include <vm/vm_map.h>
76 #include <vm/vm_kern.h>
77
78 #include <machine/spl.h>
79
80 #include <mach/shared_memory_server.h>
81 #include <vm/vm_shared_memory_server.h>
82
83 #include <vm/vm_protos.h>
84
85
86 int
87 useracc(
88 user_addr_t addr,
89 user_size_t len,
90 int prot)
91 {
92 return (vm_map_check_protection(
93 current_map(),
94 vm_map_trunc_page(addr), vm_map_round_page(addr+len),
95 prot == B_READ ? VM_PROT_READ : VM_PROT_WRITE));
96 }
97
98 int
99 vslock(
100 user_addr_t addr,
101 user_size_t len)
102 {
103 kern_return_t kret;
104 kret = vm_map_wire(current_map(), vm_map_trunc_page(addr),
105 vm_map_round_page(addr+len),
106 VM_PROT_READ | VM_PROT_WRITE ,FALSE);
107
108 switch (kret) {
109 case KERN_SUCCESS:
110 return (0);
111 case KERN_INVALID_ADDRESS:
112 case KERN_NO_SPACE:
113 return (ENOMEM);
114 case KERN_PROTECTION_FAILURE:
115 return (EACCES);
116 default:
117 return (EINVAL);
118 }
119 }
120
121 int
122 vsunlock(
123 user_addr_t addr,
124 user_size_t len,
125 __unused int dirtied)
126 {
127 #if FIXME /* [ */
128 pmap_t pmap;
129 vm_page_t pg;
130 vm_map_offset_t vaddr;
131 ppnum_t paddr;
132 #endif /* FIXME ] */
133 kern_return_t kret;
134
135 #if FIXME /* [ */
136 if (dirtied) {
137 pmap = get_task_pmap(current_task());
138 for (vaddr = vm_map_trunc_page(addr);
139 vaddr < vm_map_round_page(addr+len);
140 vaddr += PAGE_SIZE) {
141 paddr = pmap_extract(pmap, vaddr);
142 pg = PHYS_TO_VM_PAGE(paddr);
143 vm_page_set_modified(pg);
144 }
145 }
146 #endif /* FIXME ] */
147 #ifdef lint
148 dirtied++;
149 #endif /* lint */
150 kret = vm_map_unwire(current_map(), vm_map_trunc_page(addr),
151 vm_map_round_page(addr+len), FALSE);
152 switch (kret) {
153 case KERN_SUCCESS:
154 return (0);
155 case KERN_INVALID_ADDRESS:
156 case KERN_NO_SPACE:
157 return (ENOMEM);
158 case KERN_PROTECTION_FAILURE:
159 return (EACCES);
160 default:
161 return (EINVAL);
162 }
163 }
164
165 int
166 subyte(
167 user_addr_t addr,
168 int byte)
169 {
170 char character;
171
172 character = (char)byte;
173 return (copyout((void *)&(character), addr, sizeof(char)) == 0 ? 0 : -1);
174 }
175
176 int
177 suibyte(
178 user_addr_t addr,
179 int byte)
180 {
181 char character;
182
183 character = (char)byte;
184 return (copyout((void *)&(character), addr, sizeof(char)) == 0 ? 0 : -1);
185 }
186
187 int fubyte(user_addr_t addr)
188 {
189 unsigned char byte;
190
191 if (copyin(addr, (void *) &byte, sizeof(char)))
192 return(-1);
193 return(byte);
194 }
195
196 int fuibyte(user_addr_t addr)
197 {
198 unsigned char byte;
199
200 if (copyin(addr, (void *) &(byte), sizeof(char)))
201 return(-1);
202 return(byte);
203 }
204
205 int
206 suword(
207 user_addr_t addr,
208 long word)
209 {
210 return (copyout((void *) &word, addr, sizeof(int)) == 0 ? 0 : -1);
211 }
212
213 long fuword(user_addr_t addr)
214 {
215 long word;
216
217 if (copyin(addr, (void *) &word, sizeof(int)))
218 return(-1);
219 return(word);
220 }
221
222 /* suiword and fuiword are the same as suword and fuword, respectively */
223
224 int
225 suiword(
226 user_addr_t addr,
227 long word)
228 {
229 return (copyout((void *) &word, addr, sizeof(int)) == 0 ? 0 : -1);
230 }
231
232 long fuiword(user_addr_t addr)
233 {
234 long word;
235
236 if (copyin(addr, (void *) &word, sizeof(int)))
237 return(-1);
238 return(word);
239 }
240
241 /*
242 * With a 32-bit kernel and mixed 32/64-bit user tasks, this interface allows the
243 * fetching and setting of process-sized size_t and pointer values.
244 */
245 int
246 sulong(user_addr_t addr, int64_t word)
247 {
248
249 if (IS_64BIT_PROCESS(current_proc())) {
250 return(copyout((void *)&word, addr, sizeof(word)) == 0 ? 0 : -1);
251 } else {
252 return(suiword(addr, (long)word));
253 }
254 }
255
256 int64_t
257 fulong(user_addr_t addr)
258 {
259 int64_t longword;
260
261 if (IS_64BIT_PROCESS(current_proc())) {
262 if (copyin(addr, (void *)&longword, sizeof(longword)) != 0)
263 return(-1);
264 return(longword);
265 } else {
266 return((int64_t)fuiword(addr));
267 }
268 }
269
270 int
271 suulong(user_addr_t addr, uint64_t uword)
272 {
273
274 if (IS_64BIT_PROCESS(current_proc())) {
275 return(copyout((void *)&uword, addr, sizeof(uword)) == 0 ? 0 : -1);
276 } else {
277 return(suiword(addr, (u_long)uword));
278 }
279 }
280
281 uint64_t
282 fuulong(user_addr_t addr)
283 {
284 uint64_t ulongword;
285
286 if (IS_64BIT_PROCESS(current_proc())) {
287 if (copyin(addr, (void *)&ulongword, sizeof(ulongword)) != 0)
288 return(-1ULL);
289 return(ulongword);
290 } else {
291 return((uint64_t)fuiword(addr));
292 }
293 }
294
295 int
296 swapon(__unused struct proc *procp, __unused struct swapon_args *uap, __unused int *retval)
297 {
298 return(ENOTSUP);
299 }
300
301
302 kern_return_t
303 pid_for_task(
304 struct pid_for_task_args *args)
305 {
306 mach_port_name_t t = args->t;
307 user_addr_t pid_addr = args->pid;
308 struct proc * p;
309 task_t t1;
310 int pid = -1;
311 kern_return_t err = KERN_SUCCESS;
312 boolean_t funnel_state;
313
314 AUDIT_MACH_SYSCALL_ENTER(AUE_PIDFORTASK);
315 AUDIT_ARG(mach_port1, t);
316
317 funnel_state = thread_funnel_set(kernel_flock, TRUE);
318 t1 = port_name_to_task(t);
319
320 if (t1 == TASK_NULL) {
321 err = KERN_FAILURE;
322 goto pftout;
323 } else {
324 p = get_bsdtask_info(t1);
325 if (p) {
326 pid = proc_pid(p);
327 err = KERN_SUCCESS;
328 } else {
329 err = KERN_FAILURE;
330 }
331 }
332 task_deallocate(t1);
333 pftout:
334 AUDIT_ARG(pid, pid);
335 (void) copyout((char *) &pid, pid_addr, sizeof(int));
336 thread_funnel_set(kernel_flock, funnel_state);
337 AUDIT_MACH_SYSCALL_EXIT(err);
338 return(err);
339 }
340
341 /*
342 * Routine: task_for_pid
343 * Purpose:
344 * Get the task port for another "process", named by its
345 * process ID on the same host as "target_task".
346 *
347 * Only permitted to privileged processes, or processes
348 * with the same user ID.
349 *
350 * XXX This should be a BSD system call, not a Mach trap!!!
351 */
352 kern_return_t
353 task_for_pid(
354 struct task_for_pid_args *args)
355 {
356 mach_port_name_t target_tport = args->target_tport;
357 int pid = args->pid;
358 user_addr_t task_addr = args->t;
359 struct uthread *uthread;
360 struct proc *p;
361 struct proc *p1;
362 task_t t1;
363 mach_port_name_t tret;
364 void * sright;
365 int error = 0;
366 boolean_t funnel_state;
367
368 AUDIT_MACH_SYSCALL_ENTER(AUE_TASKFORPID);
369 AUDIT_ARG(pid, pid);
370 AUDIT_ARG(mach_port1, target_tport);
371
372 t1 = port_name_to_task(target_tport);
373 if (t1 == TASK_NULL) {
374 (void ) copyout((char *)&t1, task_addr, sizeof(mach_port_name_t));
375 AUDIT_MACH_SYSCALL_EXIT(KERN_FAILURE);
376 return(KERN_FAILURE);
377 }
378
379 funnel_state = thread_funnel_set(kernel_flock, TRUE);
380
381 p1 = get_bsdtask_info(t1); /* XXX current proc */
382
383 /*
384 * Delayed binding of thread credential to process credential, if we
385 * are not running with an explicitly set thread credential.
386 */
387 uthread = get_bsdthread_info(current_thread());
388 if (uthread->uu_ucred != p1->p_ucred &&
389 (uthread->uu_flag & UT_SETUID) == 0) {
390 kauth_cred_t old = uthread->uu_ucred;
391 proc_lock(p1);
392 uthread->uu_ucred = p1->p_ucred;
393 kauth_cred_ref(uthread->uu_ucred);
394 proc_unlock(p1);
395 if (old != NOCRED)
396 kauth_cred_rele(old);
397 }
398
399 p = pfind(pid);
400 AUDIT_ARG(process, p);
401
402 if (
403 (p != (struct proc *) 0)
404 && (p1 != (struct proc *) 0)
405 && (
406 (p1 == p)
407 || !(suser(kauth_cred_get(), 0))
408 || ((kauth_cred_getuid(p->p_ucred) == kauth_cred_getuid(kauth_cred_get()))
409 && (p->p_ucred->cr_ruid == kauth_cred_get()->cr_ruid)
410 && ((p->p_flag & P_SUGID) == 0))
411 )
412 && (p->p_stat != SZOMB)
413 ) {
414 if (p->task != TASK_NULL) {
415 task_reference(p->task);
416 sright = (void *)convert_task_to_port(p->task);
417 tret = ipc_port_copyout_send(
418 sright,
419 get_task_ipcspace(current_task()));
420 } else
421 tret = MACH_PORT_NULL;
422 AUDIT_ARG(mach_port2, tret);
423 (void ) copyout((char *)&tret, task_addr, sizeof(mach_port_name_t));
424 task_deallocate(t1);
425 error = KERN_SUCCESS;
426 goto tfpout;
427 }
428 task_deallocate(t1);
429 tret = MACH_PORT_NULL;
430 (void) copyout((char *) &tret, task_addr, sizeof(mach_port_name_t));
431 error = KERN_FAILURE;
432 tfpout:
433 thread_funnel_set(kernel_flock, funnel_state);
434 AUDIT_MACH_SYSCALL_EXIT(error);
435 return(error);
436 }
437
438
439 /*
440 * Try and cap the number of mappings the user might be trying to deal with,
441 * so that we don't end up allocating insane amounts of wired memory in the
442 * kernel based on bogus user arguments.
443 * There are 2 shared regions (TEXT and DATA). The size of each submap
444 * is SHARED_TEXT_REGION_SIZE and we can have at most 1 VM map entry per page,
445 * so the maximum number of mappings we could ever have to deal with is...
446 */
447 #define SHARED_REGION_MAX_MAPPINGS ((2 *SHARED_TEXT_REGION_SIZE) >> PAGE_SHIFT)
448
449 /*
450 * shared_region_make_private_np:
451 *
452 * This system call is for "dyld" only.
453 *
454 * It creates a private copy of the current process's "shared region" for
455 * split libraries. "dyld" uses this when the shared region is full or
456 * it needs to load a split library that conflicts with an already loaded one
457 * that this process doesn't need. "dyld" specifies a set of address ranges
458 * that it wants to keep in the now-private "shared region". These cover
459 * the set of split libraries that the process needs so far. The kernel needs
460 * to deallocate the rest of the shared region, so that it's available for
461 * more libraries for this process.
462 */
463 int
464 shared_region_make_private_np(
465 struct proc *p,
466 struct shared_region_make_private_np_args *uap,
467 __unused int *retvalp)
468 {
469 int error;
470 kern_return_t kr;
471 boolean_t using_shared_regions;
472 user_addr_t user_ranges;
473 unsigned int range_count;
474 vm_size_t ranges_size;
475 struct shared_region_range_np *ranges;
476 shared_region_mapping_t shared_region;
477 struct shared_region_task_mappings task_mapping_info;
478 shared_region_mapping_t next;
479
480 ranges = NULL;
481
482 range_count = uap->rangeCount;
483 user_ranges = uap->ranges;
484 ranges_size = (vm_size_t) (range_count * sizeof (ranges[0]));
485
486 /* allocate kernel space for the "ranges" */
487 if (range_count != 0) {
488 if (range_count > SHARED_REGION_MAX_MAPPINGS) {
489 error = EINVAL;
490 goto done;
491 }
492 if ((mach_vm_size_t) ranges_size !=
493 (mach_vm_size_t) range_count * sizeof (ranges[0])) {
494 /* 32-bit integer overflow */
495 error = EINVAL;
496 goto done;
497 }
498 kr = kmem_alloc(kernel_map,
499 (vm_offset_t *) &ranges,
500 ranges_size);
501 if (kr != KERN_SUCCESS) {
502 error = ENOMEM;
503 goto done;
504 }
505
506 /* copy "ranges" from user-space */
507 error = copyin(user_ranges,
508 ranges,
509 ranges_size);
510 if (error) {
511 goto done;
512 }
513 }
514
515 if (p->p_flag & P_NOSHLIB) {
516 /* no split library has been mapped for this process so far */
517 using_shared_regions = FALSE;
518 } else {
519 /* this process has already mapped some split libraries */
520 using_shared_regions = TRUE;
521 }
522
523 /*
524 * Get a private copy of the current shared region.
525 * Do not chain it to the system-wide shared region, as we'll want
526 * to map other split libraries in place of the old ones. We want
527 * to completely detach from the system-wide shared region and go our
528 * own way after this point, not sharing anything with other processes.
529 */
530 error = clone_system_shared_regions(using_shared_regions,
531 FALSE, /* chain_regions */
532 ENV_DEFAULT_ROOT);
533 if (error) {
534 goto done;
535 }
536
537 /* get info on the newly allocated shared region */
538 vm_get_shared_region(current_task(), &shared_region);
539 task_mapping_info.self = (vm_offset_t) shared_region;
540 shared_region_mapping_info(shared_region,
541 &(task_mapping_info.text_region),
542 &(task_mapping_info.text_size),
543 &(task_mapping_info.data_region),
544 &(task_mapping_info.data_size),
545 &(task_mapping_info.region_mappings),
546 &(task_mapping_info.client_base),
547 &(task_mapping_info.alternate_base),
548 &(task_mapping_info.alternate_next),
549 &(task_mapping_info.fs_base),
550 &(task_mapping_info.system),
551 &(task_mapping_info.flags),
552 &next);
553
554 /*
555 * We now have our private copy of the shared region, as it was before
556 * the call to clone_system_shared_regions(). We now need to clean it
557 * up and keep only the memory areas described by the "ranges" array.
558 */
559 kr = shared_region_cleanup(range_count, ranges, &task_mapping_info);
560 switch (kr) {
561 case KERN_SUCCESS:
562 error = 0;
563 break;
564 default:
565 error = EINVAL;
566 goto done;
567 }
568
569 done:
570 if (ranges != NULL) {
571 kmem_free(kernel_map,
572 (vm_offset_t) ranges,
573 ranges_size);
574 ranges = NULL;
575 }
576
577 return error;
578 }
579
580
581 /*
582 * shared_region_map_file_np:
583 *
584 * This system call is for "dyld" only.
585 *
586 * "dyld" wants to map parts of a split library in the shared region.
587 * We get a file descriptor on the split library to be mapped and a set
588 * of mapping instructions, describing which parts of the file to map in\
589 * which areas of the shared segment and with what protection.
590 * The "shared region" is split in 2 areas:
591 * 0x90000000 - 0xa0000000 : read-only area (for TEXT and LINKEDIT sections),
592 * 0xa0000000 - 0xb0000000 : writable area (for DATA sections).
593 *
594 */
595 int
596 shared_region_map_file_np(
597 struct proc *p,
598 struct shared_region_map_file_np_args *uap,
599 __unused int *retvalp)
600 {
601 int error;
602 kern_return_t kr;
603 int fd;
604 unsigned int mapping_count;
605 user_addr_t user_mappings; /* 64-bit */
606 user_addr_t user_slide_p; /* 64-bit */
607 struct shared_file_mapping_np *mappings;
608 vm_size_t mappings_size;
609 struct fileproc *fp;
610 mach_vm_offset_t slide;
611 struct vnode *vp;
612 struct vfs_context context;
613 memory_object_control_t file_control;
614 memory_object_size_t file_size;
615 shared_region_mapping_t shared_region;
616 struct shared_region_task_mappings task_mapping_info;
617 shared_region_mapping_t next;
618 shared_region_mapping_t default_shared_region;
619 boolean_t using_default_region;
620 unsigned int j;
621 vm_prot_t max_prot;
622 mach_vm_offset_t base_offset, end_offset;
623 mach_vm_offset_t original_base_offset;
624 boolean_t mappings_in_segment;
625 #define SFM_MAX_STACK 6
626 struct shared_file_mapping_np stack_mappings[SFM_MAX_STACK];
627
628 mappings_size = 0;
629 mappings = NULL;
630 mapping_count = 0;
631 fp = NULL;
632 vp = NULL;
633
634 /* get file descriptor for split library from arguments */
635 fd = uap->fd;
636
637 /* get file structure from file descriptor */
638 error = fp_lookup(p, fd, &fp, 0);
639 if (error) {
640 goto done;
641 }
642
643 /* make sure we're attempting to map a vnode */
644 if (fp->f_fglob->fg_type != DTYPE_VNODE) {
645 error = EINVAL;
646 goto done;
647 }
648
649 /* we need at least read permission on the file */
650 if (! (fp->f_fglob->fg_flag & FREAD)) {
651 error = EPERM;
652 goto done;
653 }
654
655 /* get vnode from file structure */
656 error = vnode_getwithref((vnode_t)fp->f_fglob->fg_data);
657 if (error) {
658 goto done;
659 }
660 vp = (struct vnode *) fp->f_fglob->fg_data;
661
662 /* make sure the vnode is a regular file */
663 if (vp->v_type != VREG) {
664 error = EINVAL;
665 goto done;
666 }
667
668 /* get vnode size */
669 {
670 off_t fs;
671
672 context.vc_proc = p;
673 context.vc_ucred = kauth_cred_get();
674 if ((error = vnode_size(vp, &fs, &context)) != 0)
675 goto done;
676 file_size = fs;
677 }
678
679 /*
680 * Get the list of mappings the caller wants us to establish.
681 */
682 mapping_count = uap->mappingCount; /* the number of mappings */
683 mappings_size = (vm_size_t) (mapping_count * sizeof (mappings[0]));
684 if (mapping_count == 0) {
685 error = 0; /* no mappings: we're done ! */
686 goto done;
687 } else if (mapping_count <= SFM_MAX_STACK) {
688 mappings = &stack_mappings[0];
689 } else {
690 if (mapping_count > SHARED_REGION_MAX_MAPPINGS) {
691 error = EINVAL;
692 goto done;
693 }
694 if ((mach_vm_size_t) mappings_size !=
695 (mach_vm_size_t) mapping_count * sizeof (mappings[0])) {
696 /* 32-bit integer overflow */
697 error = EINVAL;
698 goto done;
699 }
700 kr = kmem_alloc(kernel_map,
701 (vm_offset_t *) &mappings,
702 mappings_size);
703 if (kr != KERN_SUCCESS) {
704 error = ENOMEM;
705 goto done;
706 }
707 }
708
709 user_mappings = uap->mappings; /* the mappings, in user space */
710 error = copyin(user_mappings,
711 mappings,
712 mappings_size);
713 if (error != 0) {
714 goto done;
715 }
716
717 /*
718 * If the caller provides a "slide" pointer, it means they're OK
719 * with us moving the mappings around to make them fit.
720 */
721 user_slide_p = uap->slide_p;
722
723 /*
724 * Make each mapping address relative to the beginning of the
725 * shared region. Check that all mappings are in the shared region.
726 * Compute the maximum set of protections required to tell the
727 * buffer cache how we mapped the file (see call to ubc_map() below).
728 */
729 max_prot = VM_PROT_NONE;
730 base_offset = -1LL;
731 end_offset = 0;
732 mappings_in_segment = TRUE;
733 for (j = 0; j < mapping_count; j++) {
734 mach_vm_offset_t segment;
735 segment = (mappings[j].sfm_address &
736 GLOBAL_SHARED_SEGMENT_MASK);
737 if (segment != GLOBAL_SHARED_TEXT_SEGMENT &&
738 segment != GLOBAL_SHARED_DATA_SEGMENT) {
739 /* this mapping is not in the shared region... */
740 if (user_slide_p == NULL) {
741 /* ... and we can't slide it in: fail */
742 error = EINVAL;
743 goto done;
744 }
745 if (j == 0) {
746 /* expect all mappings to be outside */
747 mappings_in_segment = FALSE;
748 } else if (mappings_in_segment != FALSE) {
749 /* other mappings were not outside: fail */
750 error = EINVAL;
751 goto done;
752 }
753 /* we'll try and slide that mapping in the segments */
754 } else {
755 if (j == 0) {
756 /* expect all mappings to be inside */
757 mappings_in_segment = TRUE;
758 } else if (mappings_in_segment != TRUE) {
759 /* other mappings were not inside: fail */
760 error = EINVAL;
761 goto done;
762 }
763 /* get a relative offset inside the shared segments */
764 mappings[j].sfm_address -= GLOBAL_SHARED_TEXT_SEGMENT;
765 }
766 if ((mappings[j].sfm_address & SHARED_TEXT_REGION_MASK)
767 < base_offset) {
768 base_offset = (mappings[j].sfm_address &
769 SHARED_TEXT_REGION_MASK);
770 }
771 if ((mappings[j].sfm_address & SHARED_TEXT_REGION_MASK) +
772 mappings[j].sfm_size > end_offset) {
773 end_offset =
774 (mappings[j].sfm_address &
775 SHARED_TEXT_REGION_MASK) +
776 mappings[j].sfm_size;
777 }
778 max_prot |= mappings[j].sfm_max_prot;
779 }
780 /* Make all mappings relative to the base_offset */
781 base_offset = vm_map_trunc_page(base_offset);
782 end_offset = vm_map_round_page(end_offset);
783 for (j = 0; j < mapping_count; j++) {
784 mappings[j].sfm_address -= base_offset;
785 }
786 original_base_offset = base_offset;
787 if (mappings_in_segment == FALSE) {
788 /*
789 * We're trying to map a library that was not pre-bound to
790 * be in the shared segments. We want to try and slide it
791 * back into the shared segments but as far back as possible,
792 * so that it doesn't clash with pre-bound libraries. Set
793 * the base_offset to the end of the region, so that it can't
794 * possibly fit there and will have to be slid.
795 */
796 base_offset = SHARED_TEXT_REGION_SIZE - end_offset;
797 }
798
799 /* get the file's memory object handle */
800 UBCINFOCHECK("shared_region_map_file_np", vp);
801 file_control = ubc_getobject(vp, UBC_HOLDOBJECT);
802 if (file_control == MEMORY_OBJECT_CONTROL_NULL) {
803 error = EINVAL;
804 goto done;
805 }
806
807 /*
808 * Get info about the current process's shared region.
809 * This might change if we decide we need to clone the shared region.
810 */
811 vm_get_shared_region(current_task(), &shared_region);
812 task_mapping_info.self = (vm_offset_t) shared_region;
813 shared_region_mapping_info(shared_region,
814 &(task_mapping_info.text_region),
815 &(task_mapping_info.text_size),
816 &(task_mapping_info.data_region),
817 &(task_mapping_info.data_size),
818 &(task_mapping_info.region_mappings),
819 &(task_mapping_info.client_base),
820 &(task_mapping_info.alternate_base),
821 &(task_mapping_info.alternate_next),
822 &(task_mapping_info.fs_base),
823 &(task_mapping_info.system),
824 &(task_mapping_info.flags),
825 &next);
826
827 /*
828 * Are we using the system's current shared region
829 * for this environment ?
830 */
831 default_shared_region =
832 lookup_default_shared_region(ENV_DEFAULT_ROOT,
833 task_mapping_info.system);
834 if (shared_region == default_shared_region) {
835 using_default_region = TRUE;
836 } else {
837 using_default_region = FALSE;
838 }
839 shared_region_mapping_dealloc(default_shared_region);
840
841 if (vp->v_mount != rootvnode->v_mount &&
842 using_default_region) {
843 /*
844 * The split library is not on the root filesystem. We don't
845 * want to polute the system-wide ("default") shared region
846 * with it.
847 * Reject the mapping. The caller (dyld) should "privatize"
848 * (via shared_region_make_private()) the shared region and
849 * try to establish the mapping privately for this process.
850 */
851 error = EXDEV;
852 goto done;
853 }
854
855
856 /*
857 * Map the split library.
858 */
859 kr = map_shared_file(mapping_count,
860 mappings,
861 file_control,
862 file_size,
863 &task_mapping_info,
864 base_offset,
865 (user_slide_p) ? &slide : NULL);
866
867 switch (kr) {
868 case KERN_SUCCESS:
869 /*
870 * The mapping was successful. Let the buffer cache know
871 * that we've mapped that file with these protections. This
872 * prevents the vnode from getting recycled while it's mapped.
873 */
874 (void) ubc_map(vp, max_prot);
875 error = 0;
876 break;
877 case KERN_INVALID_ADDRESS:
878 error = EFAULT;
879 goto done;
880 case KERN_PROTECTION_FAILURE:
881 error = EPERM;
882 goto done;
883 case KERN_NO_SPACE:
884 error = ENOMEM;
885 goto done;
886 case KERN_FAILURE:
887 case KERN_INVALID_ARGUMENT:
888 default:
889 error = EINVAL;
890 goto done;
891 }
892
893 if (p->p_flag & P_NOSHLIB) {
894 /* signal that this process is now using split libraries */
895 p->p_flag &= ~P_NOSHLIB;
896 }
897
898 if (user_slide_p) {
899 /*
900 * The caller provided a pointer to a "slide" offset. Let
901 * them know by how much we slid the mappings.
902 */
903 if (mappings_in_segment == FALSE) {
904 /*
905 * We faked the base_offset earlier, so undo that
906 * and take into account the real base_offset.
907 */
908 slide += SHARED_TEXT_REGION_SIZE - end_offset;
909 slide -= original_base_offset;
910 /*
911 * The mappings were slid into the shared segments
912 * and "slide" is relative to the beginning of the
913 * shared segments. Adjust it to be absolute.
914 */
915 slide += GLOBAL_SHARED_TEXT_SEGMENT;
916 }
917 error = copyout(&slide,
918 user_slide_p,
919 sizeof (int64_t));
920 }
921
922 done:
923 if (vp != NULL) {
924 /*
925 * release the vnode...
926 * ubc_map() still holds it for us in the non-error case
927 */
928 (void) vnode_put(vp);
929 vp = NULL;
930 }
931 if (fp != NULL) {
932 /* release the file descriptor */
933 fp_drop(p, fd, fp, 0);
934 fp = NULL;
935 }
936 if (mappings != NULL &&
937 mappings != &stack_mappings[0]) {
938 kmem_free(kernel_map,
939 (vm_offset_t) mappings,
940 mappings_size);
941 }
942 mappings = NULL;
943
944 return error;
945 }
946
947 int
948 load_shared_file(
949 __unused struct proc *p,
950 __unused struct load_shared_file_args *uap,
951 __unused int *retval)
952 {
953 return ENOSYS;
954 }
955
956 int
957 reset_shared_file(
958 __unused struct proc *p,
959 __unused struct reset_shared_file_args *uap,
960 __unused int *retval)
961 {
962 return ENOSYS;
963 }
964
965 int
966 new_system_shared_regions(
967 __unused struct proc *p,
968 __unused struct new_system_shared_regions_args *uap,
969 __unused int *retval)
970 {
971 return ENOSYS;
972 }
973
974
975
976 int
977 clone_system_shared_regions(
978 int shared_regions_active,
979 int chain_regions,
980 int base_vnode)
981 {
982 shared_region_mapping_t new_shared_region;
983 shared_region_mapping_t next;
984 shared_region_mapping_t old_shared_region;
985 struct shared_region_task_mappings old_info;
986 struct shared_region_task_mappings new_info;
987
988 vm_get_shared_region(current_task(), &old_shared_region);
989 old_info.self = (vm_offset_t)old_shared_region;
990 shared_region_mapping_info(old_shared_region,
991 &(old_info.text_region),
992 &(old_info.text_size),
993 &(old_info.data_region),
994 &(old_info.data_size),
995 &(old_info.region_mappings),
996 &(old_info.client_base),
997 &(old_info.alternate_base),
998 &(old_info.alternate_next),
999 &(old_info.fs_base),
1000 &(old_info.system),
1001 &(old_info.flags), &next);
1002 if ((shared_regions_active) ||
1003 (base_vnode == ENV_DEFAULT_ROOT)) {
1004 if (shared_file_create_system_region(&new_shared_region))
1005 return (ENOMEM);
1006 } else {
1007 new_shared_region =
1008 lookup_default_shared_region(
1009 base_vnode, old_info.system);
1010 if(new_shared_region == NULL) {
1011 shared_file_boot_time_init(
1012 base_vnode, old_info.system);
1013 vm_get_shared_region(current_task(), &new_shared_region);
1014 } else {
1015 vm_set_shared_region(current_task(), new_shared_region);
1016 }
1017 if(old_shared_region)
1018 shared_region_mapping_dealloc(old_shared_region);
1019 }
1020 new_info.self = (vm_offset_t)new_shared_region;
1021 shared_region_mapping_info(new_shared_region,
1022 &(new_info.text_region),
1023 &(new_info.text_size),
1024 &(new_info.data_region),
1025 &(new_info.data_size),
1026 &(new_info.region_mappings),
1027 &(new_info.client_base),
1028 &(new_info.alternate_base),
1029 &(new_info.alternate_next),
1030 &(new_info.fs_base),
1031 &(new_info.system),
1032 &(new_info.flags), &next);
1033 if(shared_regions_active) {
1034 if(vm_region_clone(old_info.text_region, new_info.text_region)) {
1035 panic("clone_system_shared_regions: shared region mis-alignment 1");
1036 shared_region_mapping_dealloc(new_shared_region);
1037 return(EINVAL);
1038 }
1039 if (vm_region_clone(old_info.data_region, new_info.data_region)) {
1040 panic("clone_system_shared_regions: shared region mis-alignment 2");
1041 shared_region_mapping_dealloc(new_shared_region);
1042 return(EINVAL);
1043 }
1044 if (chain_regions) {
1045 /*
1046 * We want a "shadowed" clone, a private superset of the old
1047 * shared region. The info about the old mappings is still
1048 * valid for us.
1049 */
1050 shared_region_object_chain_attach(
1051 new_shared_region, old_shared_region);
1052 } else {
1053 /*
1054 * We want a completely detached clone with no link to
1055 * the old shared region. We'll be removing some mappings
1056 * in our private, cloned, shared region, so the old mappings
1057 * will become irrelevant to us. Since we have a private
1058 * "shared region" now, it isn't going to be shared with
1059 * anyone else and we won't need to maintain mappings info.
1060 */
1061 shared_region_object_chain_detached(new_shared_region);
1062 }
1063 }
1064 if (vm_map_region_replace(current_map(), old_info.text_region,
1065 new_info.text_region, old_info.client_base,
1066 old_info.client_base+old_info.text_size)) {
1067 panic("clone_system_shared_regions: shared region mis-alignment 3");
1068 shared_region_mapping_dealloc(new_shared_region);
1069 return(EINVAL);
1070 }
1071 if(vm_map_region_replace(current_map(), old_info.data_region,
1072 new_info.data_region,
1073 old_info.client_base + old_info.text_size,
1074 old_info.client_base
1075 + old_info.text_size + old_info.data_size)) {
1076 panic("clone_system_shared_regions: shared region mis-alignment 4");
1077 shared_region_mapping_dealloc(new_shared_region);
1078 return(EINVAL);
1079 }
1080 vm_set_shared_region(current_task(), new_shared_region);
1081
1082 /* consume the reference which wasn't accounted for in object */
1083 /* chain attach */
1084 if (!shared_regions_active || !chain_regions)
1085 shared_region_mapping_dealloc(old_shared_region);
1086
1087 return(0);
1088
1089 }
1090
1091 /* header for the profile name file. The profiled app info is held */
1092 /* in the data file and pointed to by elements in the name file */
1093
1094 struct profile_names_header {
1095 unsigned int number_of_profiles;
1096 unsigned int user_id;
1097 unsigned int version;
1098 off_t element_array;
1099 unsigned int spare1;
1100 unsigned int spare2;
1101 unsigned int spare3;
1102 };
1103
1104 struct profile_element {
1105 off_t addr;
1106 vm_size_t size;
1107 unsigned int mod_date;
1108 unsigned int inode;
1109 char name[12];
1110 };
1111
1112 struct global_profile {
1113 struct vnode *names_vp;
1114 struct vnode *data_vp;
1115 vm_offset_t buf_ptr;
1116 unsigned int user;
1117 unsigned int age;
1118 unsigned int busy;
1119 };
1120
1121 struct global_profile_cache {
1122 int max_ele;
1123 unsigned int age;
1124 struct global_profile profiles[3];
1125 };
1126
1127 /* forward declarations */
1128 int bsd_open_page_cache_files(unsigned int user,
1129 struct global_profile **profile);
1130 void bsd_close_page_cache_files(struct global_profile *profile);
1131 int bsd_search_page_cache_data_base(
1132 struct vnode *vp,
1133 struct profile_names_header *database,
1134 char *app_name,
1135 unsigned int mod_date,
1136 unsigned int inode,
1137 off_t *profile,
1138 unsigned int *profile_size);
1139
1140 struct global_profile_cache global_user_profile_cache =
1141 {3, 0, {{NULL, NULL, 0, 0, 0, 0},
1142 {NULL, NULL, 0, 0, 0, 0},
1143 {NULL, NULL, 0, 0, 0, 0}} };
1144
1145 /* BSD_OPEN_PAGE_CACHE_FILES: */
1146 /* Caller provides a user id. This id was used in */
1147 /* prepare_profile_database to create two unique absolute */
1148 /* file paths to the associated profile files. These files */
1149 /* are either opened or bsd_open_page_cache_files returns an */
1150 /* error. The header of the names file is then consulted. */
1151 /* The header and the vnodes for the names and data files are */
1152 /* returned. */
1153
1154 int
1155 bsd_open_page_cache_files(
1156 unsigned int user,
1157 struct global_profile **profile)
1158 {
1159 const char *cache_path = "/var/vm/app_profile/";
1160 struct proc *p;
1161 int error;
1162 vm_size_t resid;
1163 off_t resid_off;
1164 unsigned int lru;
1165 vm_size_t size;
1166
1167 struct vnode *names_vp;
1168 struct vnode *data_vp;
1169 vm_offset_t names_buf;
1170 vm_offset_t buf_ptr;
1171
1172 int profile_names_length;
1173 int profile_data_length;
1174 char *profile_data_string;
1175 char *profile_names_string;
1176 char *substring;
1177
1178 off_t file_size;
1179 struct vfs_context context;
1180
1181 kern_return_t ret;
1182
1183 struct nameidata nd_names;
1184 struct nameidata nd_data;
1185 int i;
1186
1187
1188 p = current_proc();
1189
1190 context.vc_proc = p;
1191 context.vc_ucred = kauth_cred_get();
1192
1193 restart:
1194 for(i = 0; i<global_user_profile_cache.max_ele; i++) {
1195 if((global_user_profile_cache.profiles[i].user == user)
1196 && (global_user_profile_cache.profiles[i].data_vp
1197 != NULL)) {
1198 *profile = &global_user_profile_cache.profiles[i];
1199 /* already in cache, we're done */
1200 if ((*profile)->busy) {
1201 /*
1202 * drop funnel and wait
1203 */
1204 (void)tsleep((void *)
1205 *profile,
1206 PRIBIO, "app_profile", 0);
1207 goto restart;
1208 }
1209 (*profile)->busy = 1;
1210 (*profile)->age = global_user_profile_cache.age;
1211
1212 /*
1213 * entries in cache are held with a valid
1214 * usecount... take an iocount which will
1215 * be dropped in "bsd_close_page_cache_files"
1216 * which is called after the read or writes to
1217 * these files are done
1218 */
1219 if ( (vnode_getwithref((*profile)->data_vp)) ) {
1220
1221 vnode_rele((*profile)->data_vp);
1222 vnode_rele((*profile)->names_vp);
1223
1224 (*profile)->data_vp = NULL;
1225 (*profile)->busy = 0;
1226 wakeup(*profile);
1227
1228 goto restart;
1229 }
1230 if ( (vnode_getwithref((*profile)->names_vp)) ) {
1231
1232 vnode_put((*profile)->data_vp);
1233 vnode_rele((*profile)->data_vp);
1234 vnode_rele((*profile)->names_vp);
1235
1236 (*profile)->data_vp = NULL;
1237 (*profile)->busy = 0;
1238 wakeup(*profile);
1239
1240 goto restart;
1241 }
1242 global_user_profile_cache.age+=1;
1243 return 0;
1244 }
1245 }
1246
1247 lru = global_user_profile_cache.age;
1248 *profile = NULL;
1249 for(i = 0; i<global_user_profile_cache.max_ele; i++) {
1250 /* Skip entry if it is in the process of being reused */
1251 if(global_user_profile_cache.profiles[i].data_vp ==
1252 (struct vnode *)0xFFFFFFFF)
1253 continue;
1254 /* Otherwise grab the first empty entry */
1255 if(global_user_profile_cache.profiles[i].data_vp == NULL) {
1256 *profile = &global_user_profile_cache.profiles[i];
1257 (*profile)->age = global_user_profile_cache.age;
1258 break;
1259 }
1260 /* Otherwise grab the oldest entry */
1261 if(global_user_profile_cache.profiles[i].age < lru) {
1262 lru = global_user_profile_cache.profiles[i].age;
1263 *profile = &global_user_profile_cache.profiles[i];
1264 }
1265 }
1266
1267 /* Did we set it? */
1268 if (*profile == NULL) {
1269 /*
1270 * No entries are available; this can only happen if all
1271 * of them are currently in the process of being reused;
1272 * if this happens, we sleep on the address of the first
1273 * element, and restart. This is less than ideal, but we
1274 * know it will work because we know that there will be a
1275 * wakeup on any entry currently in the process of being
1276 * reused.
1277 *
1278 * XXX Reccomend a two handed clock and more than 3 total
1279 * XXX cache entries at some point in the future.
1280 */
1281 /*
1282 * drop funnel and wait
1283 */
1284 (void)tsleep((void *)
1285 &global_user_profile_cache.profiles[0],
1286 PRIBIO, "app_profile", 0);
1287 goto restart;
1288 }
1289
1290 /*
1291 * If it's currently busy, we've picked the one at the end of the
1292 * LRU list, but it's currently being actively used. We sleep on
1293 * its address and restart.
1294 */
1295 if ((*profile)->busy) {
1296 /*
1297 * drop funnel and wait
1298 */
1299 (void)tsleep((void *)
1300 *profile,
1301 PRIBIO, "app_profile", 0);
1302 goto restart;
1303 }
1304 (*profile)->busy = 1;
1305 (*profile)->user = user;
1306
1307 /*
1308 * put dummy value in for now to get competing request to wait
1309 * above until we are finished
1310 *
1311 * Save the data_vp before setting it, so we can set it before
1312 * we kmem_free() or vrele(). If we don't do this, then we
1313 * have a potential funnel race condition we have to deal with.
1314 */
1315 data_vp = (*profile)->data_vp;
1316 (*profile)->data_vp = (struct vnode *)0xFFFFFFFF;
1317
1318 /*
1319 * Age the cache here in all cases; this guarantees that we won't
1320 * be reusing only one entry over and over, once the system reaches
1321 * steady-state.
1322 */
1323 global_user_profile_cache.age+=1;
1324
1325 if(data_vp != NULL) {
1326 kmem_free(kernel_map,
1327 (*profile)->buf_ptr, 4 * PAGE_SIZE);
1328 if ((*profile)->names_vp) {
1329 vnode_rele((*profile)->names_vp);
1330 (*profile)->names_vp = NULL;
1331 }
1332 vnode_rele(data_vp);
1333 }
1334
1335 /* Try to open the appropriate users profile files */
1336 /* If neither file is present, try to create them */
1337 /* If one file is present and the other not, fail. */
1338 /* If the files do exist, check them for the app_file */
1339 /* requested and read it in if present */
1340
1341 ret = kmem_alloc(kernel_map,
1342 (vm_offset_t *)&profile_data_string, PATH_MAX);
1343
1344 if(ret) {
1345 (*profile)->data_vp = NULL;
1346 (*profile)->busy = 0;
1347 wakeup(*profile);
1348 return ENOMEM;
1349 }
1350
1351 /* Split the buffer in half since we know the size of */
1352 /* our file path and our allocation is adequate for */
1353 /* both file path names */
1354 profile_names_string = profile_data_string + (PATH_MAX/2);
1355
1356
1357 strcpy(profile_data_string, cache_path);
1358 strcpy(profile_names_string, cache_path);
1359 profile_names_length = profile_data_length
1360 = strlen(profile_data_string);
1361 substring = profile_data_string + profile_data_length;
1362 sprintf(substring, "%x_data", user);
1363 substring = profile_names_string + profile_names_length;
1364 sprintf(substring, "%x_names", user);
1365
1366 /* We now have the absolute file names */
1367
1368 ret = kmem_alloc(kernel_map,
1369 (vm_offset_t *)&names_buf, 4 * PAGE_SIZE);
1370 if(ret) {
1371 kmem_free(kernel_map,
1372 (vm_offset_t)profile_data_string, PATH_MAX);
1373 (*profile)->data_vp = NULL;
1374 (*profile)->busy = 0;
1375 wakeup(*profile);
1376 return ENOMEM;
1377 }
1378
1379 NDINIT(&nd_names, LOOKUP, FOLLOW | LOCKLEAF,
1380 UIO_SYSSPACE32, CAST_USER_ADDR_T(profile_names_string), &context);
1381 NDINIT(&nd_data, LOOKUP, FOLLOW | LOCKLEAF,
1382 UIO_SYSSPACE32, CAST_USER_ADDR_T(profile_data_string), &context);
1383
1384 if ( (error = vn_open(&nd_data, FREAD | FWRITE, 0)) ) {
1385 #ifdef notdef
1386 printf("bsd_open_page_cache_files: CacheData file not found %s\n",
1387 profile_data_string);
1388 #endif
1389 kmem_free(kernel_map,
1390 (vm_offset_t)names_buf, 4 * PAGE_SIZE);
1391 kmem_free(kernel_map,
1392 (vm_offset_t)profile_data_string, PATH_MAX);
1393 (*profile)->data_vp = NULL;
1394 (*profile)->busy = 0;
1395 wakeup(*profile);
1396 return error;
1397 }
1398 data_vp = nd_data.ni_vp;
1399
1400 if ( (error = vn_open(&nd_names, FREAD | FWRITE, 0)) ) {
1401 printf("bsd_open_page_cache_files: NamesData file not found %s\n",
1402 profile_data_string);
1403 kmem_free(kernel_map,
1404 (vm_offset_t)names_buf, 4 * PAGE_SIZE);
1405 kmem_free(kernel_map,
1406 (vm_offset_t)profile_data_string, PATH_MAX);
1407
1408 vnode_rele(data_vp);
1409 vnode_put(data_vp);
1410
1411 (*profile)->data_vp = NULL;
1412 (*profile)->busy = 0;
1413 wakeup(*profile);
1414 return error;
1415 }
1416 names_vp = nd_names.ni_vp;
1417
1418 if ((error = vnode_size(names_vp, &file_size, &context)) != 0) {
1419 printf("bsd_open_page_cache_files: Can't stat name file %s\n", profile_names_string);
1420 kmem_free(kernel_map,
1421 (vm_offset_t)profile_data_string, PATH_MAX);
1422 kmem_free(kernel_map,
1423 (vm_offset_t)names_buf, 4 * PAGE_SIZE);
1424
1425 vnode_rele(names_vp);
1426 vnode_put(names_vp);
1427 vnode_rele(data_vp);
1428 vnode_put(data_vp);
1429
1430 (*profile)->data_vp = NULL;
1431 (*profile)->busy = 0;
1432 wakeup(*profile);
1433 return error;
1434 }
1435
1436 size = file_size;
1437 if(size > 4 * PAGE_SIZE)
1438 size = 4 * PAGE_SIZE;
1439 buf_ptr = names_buf;
1440 resid_off = 0;
1441
1442 while(size) {
1443 error = vn_rdwr(UIO_READ, names_vp, (caddr_t)buf_ptr,
1444 size, resid_off,
1445 UIO_SYSSPACE32, IO_NODELOCKED, kauth_cred_get(), &resid, p);
1446 if((error) || (size == resid)) {
1447 if(!error) {
1448 error = EINVAL;
1449 }
1450 kmem_free(kernel_map,
1451 (vm_offset_t)profile_data_string, PATH_MAX);
1452 kmem_free(kernel_map,
1453 (vm_offset_t)names_buf, 4 * PAGE_SIZE);
1454
1455 vnode_rele(names_vp);
1456 vnode_put(names_vp);
1457 vnode_rele(data_vp);
1458 vnode_put(data_vp);
1459
1460 (*profile)->data_vp = NULL;
1461 (*profile)->busy = 0;
1462 wakeup(*profile);
1463 return error;
1464 }
1465 buf_ptr += size-resid;
1466 resid_off += size-resid;
1467 size = resid;
1468 }
1469 kmem_free(kernel_map, (vm_offset_t)profile_data_string, PATH_MAX);
1470
1471 (*profile)->names_vp = names_vp;
1472 (*profile)->data_vp = data_vp;
1473 (*profile)->buf_ptr = names_buf;
1474
1475 /*
1476 * at this point, the both the names_vp and the data_vp have
1477 * both a valid usecount and an iocount held
1478 */
1479 return 0;
1480
1481 }
1482
1483 void
1484 bsd_close_page_cache_files(
1485 struct global_profile *profile)
1486 {
1487 vnode_put(profile->data_vp);
1488 vnode_put(profile->names_vp);
1489
1490 profile->busy = 0;
1491 wakeup(profile);
1492 }
1493
1494 int
1495 bsd_read_page_cache_file(
1496 unsigned int user,
1497 int *fid,
1498 int *mod,
1499 char *app_name,
1500 struct vnode *app_vp,
1501 vm_offset_t *buffer,
1502 vm_offset_t *bufsize)
1503 {
1504
1505 boolean_t funnel_state;
1506
1507 struct proc *p;
1508 int error;
1509 unsigned int resid;
1510
1511 off_t profile;
1512 unsigned int profile_size;
1513
1514 vm_offset_t names_buf;
1515 struct vnode_attr va;
1516 struct vfs_context context;
1517
1518 kern_return_t ret;
1519
1520 struct vnode *names_vp;
1521 struct vnode *data_vp;
1522
1523 struct global_profile *uid_files;
1524
1525 funnel_state = thread_funnel_set(kernel_flock, TRUE);
1526
1527 /* Try to open the appropriate users profile files */
1528 /* If neither file is present, try to create them */
1529 /* If one file is present and the other not, fail. */
1530 /* If the files do exist, check them for the app_file */
1531 /* requested and read it in if present */
1532
1533
1534 error = bsd_open_page_cache_files(user, &uid_files);
1535 if(error) {
1536 thread_funnel_set(kernel_flock, funnel_state);
1537 return EINVAL;
1538 }
1539
1540 p = current_proc();
1541
1542 names_vp = uid_files->names_vp;
1543 data_vp = uid_files->data_vp;
1544 names_buf = uid_files->buf_ptr;
1545
1546 context.vc_proc = p;
1547 context.vc_ucred = kauth_cred_get();
1548
1549 VATTR_INIT(&va);
1550 VATTR_WANTED(&va, va_fileid);
1551 VATTR_WANTED(&va, va_modify_time);
1552
1553 if ((error = vnode_getattr(app_vp, &va, &context))) {
1554 printf("bsd_read_cache_file: Can't stat app file %s\n", app_name);
1555 bsd_close_page_cache_files(uid_files);
1556 thread_funnel_set(kernel_flock, funnel_state);
1557 return error;
1558 }
1559
1560 *fid = (u_long)va.va_fileid;
1561 *mod = va.va_modify_time.tv_sec;
1562
1563 if (bsd_search_page_cache_data_base(
1564 names_vp,
1565 (struct profile_names_header *)names_buf,
1566 app_name,
1567 (unsigned int) va.va_modify_time.tv_sec,
1568 (u_long)va.va_fileid, &profile, &profile_size) == 0) {
1569 /* profile is an offset in the profile data base */
1570 /* It is zero if no profile data was found */
1571
1572 if(profile_size == 0) {
1573 *buffer = 0;
1574 *bufsize = 0;
1575 bsd_close_page_cache_files(uid_files);
1576 thread_funnel_set(kernel_flock, funnel_state);
1577 return 0;
1578 }
1579 ret = (vm_offset_t)(kmem_alloc(kernel_map, buffer, profile_size));
1580 if(ret) {
1581 bsd_close_page_cache_files(uid_files);
1582 thread_funnel_set(kernel_flock, funnel_state);
1583 return ENOMEM;
1584 }
1585 *bufsize = profile_size;
1586 while(profile_size) {
1587 error = vn_rdwr(UIO_READ, data_vp,
1588 (caddr_t) *buffer, profile_size,
1589 profile, UIO_SYSSPACE32, IO_NODELOCKED,
1590 kauth_cred_get(), &resid, p);
1591 if((error) || (profile_size == resid)) {
1592 bsd_close_page_cache_files(uid_files);
1593 kmem_free(kernel_map, (vm_offset_t)*buffer, profile_size);
1594 thread_funnel_set(kernel_flock, funnel_state);
1595 return EINVAL;
1596 }
1597 profile += profile_size - resid;
1598 profile_size = resid;
1599 }
1600 bsd_close_page_cache_files(uid_files);
1601 thread_funnel_set(kernel_flock, funnel_state);
1602 return 0;
1603 } else {
1604 bsd_close_page_cache_files(uid_files);
1605 thread_funnel_set(kernel_flock, funnel_state);
1606 return EINVAL;
1607 }
1608
1609 }
1610
1611 int
1612 bsd_search_page_cache_data_base(
1613 struct vnode *vp,
1614 struct profile_names_header *database,
1615 char *app_name,
1616 unsigned int mod_date,
1617 unsigned int inode,
1618 off_t *profile,
1619 unsigned int *profile_size)
1620 {
1621
1622 struct proc *p;
1623
1624 unsigned int i;
1625 struct profile_element *element;
1626 unsigned int ele_total;
1627 unsigned int extended_list = 0;
1628 off_t file_off = 0;
1629 unsigned int size;
1630 off_t resid_off;
1631 unsigned int resid;
1632 vm_offset_t local_buf = 0;
1633
1634 int error;
1635 kern_return_t ret;
1636
1637 p = current_proc();
1638
1639 if(((vm_offset_t)database->element_array) !=
1640 sizeof(struct profile_names_header)) {
1641 return EINVAL;
1642 }
1643 element = (struct profile_element *)(
1644 (vm_offset_t)database->element_array +
1645 (vm_offset_t)database);
1646
1647 ele_total = database->number_of_profiles;
1648
1649 *profile = 0;
1650 *profile_size = 0;
1651 while(ele_total) {
1652 /* note: code assumes header + n*ele comes out on a page boundary */
1653 if(((local_buf == 0) && (sizeof(struct profile_names_header) +
1654 (ele_total * sizeof(struct profile_element)))
1655 > (PAGE_SIZE * 4)) ||
1656 ((local_buf != 0) &&
1657 (ele_total * sizeof(struct profile_element))
1658 > (PAGE_SIZE * 4))) {
1659 extended_list = ele_total;
1660 if(element == (struct profile_element *)
1661 ((vm_offset_t)database->element_array +
1662 (vm_offset_t)database)) {
1663 ele_total = ((PAGE_SIZE * 4)/sizeof(struct profile_element)) - 1;
1664 } else {
1665 ele_total = (PAGE_SIZE * 4)/sizeof(struct profile_element);
1666 }
1667 extended_list -= ele_total;
1668 }
1669 for (i=0; i<ele_total; i++) {
1670 if((mod_date == element[i].mod_date)
1671 && (inode == element[i].inode)) {
1672 if(strncmp(element[i].name, app_name, 12) == 0) {
1673 *profile = element[i].addr;
1674 *profile_size = element[i].size;
1675 if(local_buf != 0) {
1676 kmem_free(kernel_map, local_buf, 4 * PAGE_SIZE);
1677 }
1678 return 0;
1679 }
1680 }
1681 }
1682 if(extended_list == 0)
1683 break;
1684 if(local_buf == 0) {
1685 ret = kmem_alloc(kernel_map, &local_buf, 4 * PAGE_SIZE);
1686 if(ret != KERN_SUCCESS) {
1687 return ENOMEM;
1688 }
1689 }
1690 element = (struct profile_element *)local_buf;
1691 ele_total = extended_list;
1692 extended_list = 0;
1693 file_off += 4 * PAGE_SIZE;
1694 if((ele_total * sizeof(struct profile_element)) >
1695 (PAGE_SIZE * 4)) {
1696 size = PAGE_SIZE * 4;
1697 } else {
1698 size = ele_total * sizeof(struct profile_element);
1699 }
1700 resid_off = 0;
1701 while(size) {
1702 error = vn_rdwr(UIO_READ, vp,
1703 CAST_DOWN(caddr_t, (local_buf + resid_off)),
1704 size, file_off + resid_off, UIO_SYSSPACE32,
1705 IO_NODELOCKED, kauth_cred_get(), &resid, p);
1706 if((error) || (size == resid)) {
1707 if(local_buf != 0) {
1708 kmem_free(kernel_map, local_buf, 4 * PAGE_SIZE);
1709 }
1710 return EINVAL;
1711 }
1712 resid_off += size-resid;
1713 size = resid;
1714 }
1715 }
1716 if(local_buf != 0) {
1717 kmem_free(kernel_map, local_buf, 4 * PAGE_SIZE);
1718 }
1719 return 0;
1720 }
1721
1722 int
1723 bsd_write_page_cache_file(
1724 unsigned int user,
1725 char *file_name,
1726 caddr_t buffer,
1727 vm_size_t size,
1728 int mod,
1729 int fid)
1730 {
1731 struct proc *p;
1732 int resid;
1733 off_t resid_off;
1734 int error;
1735 boolean_t funnel_state;
1736 off_t file_size;
1737 struct vfs_context context;
1738 off_t profile;
1739 unsigned int profile_size;
1740
1741 vm_offset_t names_buf;
1742 struct vnode *names_vp;
1743 struct vnode *data_vp;
1744 struct profile_names_header *profile_header;
1745 off_t name_offset;
1746 struct global_profile *uid_files;
1747
1748
1749 funnel_state = thread_funnel_set(kernel_flock, TRUE);
1750
1751
1752 error = bsd_open_page_cache_files(user, &uid_files);
1753 if(error) {
1754 thread_funnel_set(kernel_flock, funnel_state);
1755 return EINVAL;
1756 }
1757
1758 p = current_proc();
1759
1760 names_vp = uid_files->names_vp;
1761 data_vp = uid_files->data_vp;
1762 names_buf = uid_files->buf_ptr;
1763
1764 /* Stat data file for size */
1765
1766 context.vc_proc = p;
1767 context.vc_ucred = kauth_cred_get();
1768
1769 if ((error = vnode_size(data_vp, &file_size, &context)) != 0) {
1770 printf("bsd_write_page_cache_file: Can't stat profile data %s\n", file_name);
1771 bsd_close_page_cache_files(uid_files);
1772 thread_funnel_set(kernel_flock, funnel_state);
1773 return error;
1774 }
1775
1776 if (bsd_search_page_cache_data_base(names_vp,
1777 (struct profile_names_header *)names_buf,
1778 file_name, (unsigned int) mod,
1779 fid, &profile, &profile_size) == 0) {
1780 /* profile is an offset in the profile data base */
1781 /* It is zero if no profile data was found */
1782
1783 if(profile_size == 0) {
1784 unsigned int header_size;
1785 vm_offset_t buf_ptr;
1786
1787 /* Our Write case */
1788
1789 /* read header for last entry */
1790 profile_header =
1791 (struct profile_names_header *)names_buf;
1792 name_offset = sizeof(struct profile_names_header) +
1793 (sizeof(struct profile_element)
1794 * profile_header->number_of_profiles);
1795 profile_header->number_of_profiles += 1;
1796
1797 if(name_offset < PAGE_SIZE * 4) {
1798 struct profile_element *name;
1799 /* write new entry */
1800 name = (struct profile_element *)
1801 (names_buf + (vm_offset_t)name_offset);
1802 name->addr = file_size;
1803 name->size = size;
1804 name->mod_date = mod;
1805 name->inode = fid;
1806 strncpy (name->name, file_name, 12);
1807 } else {
1808 unsigned int ele_size;
1809 struct profile_element name;
1810 /* write new entry */
1811 name.addr = file_size;
1812 name.size = size;
1813 name.mod_date = mod;
1814 name.inode = fid;
1815 strncpy (name.name, file_name, 12);
1816 /* write element out separately */
1817 ele_size = sizeof(struct profile_element);
1818 buf_ptr = (vm_offset_t)&name;
1819 resid_off = name_offset;
1820
1821 while(ele_size) {
1822 error = vn_rdwr(UIO_WRITE, names_vp,
1823 (caddr_t)buf_ptr,
1824 ele_size, resid_off,
1825 UIO_SYSSPACE32, IO_NODELOCKED,
1826 kauth_cred_get(), &resid, p);
1827 if(error) {
1828 printf("bsd_write_page_cache_file: Can't write name_element %x\n", user);
1829 bsd_close_page_cache_files(
1830 uid_files);
1831 thread_funnel_set(
1832 kernel_flock,
1833 funnel_state);
1834 return error;
1835 }
1836 buf_ptr += (vm_offset_t)
1837 ele_size-resid;
1838 resid_off += ele_size-resid;
1839 ele_size = resid;
1840 }
1841 }
1842
1843 if(name_offset < PAGE_SIZE * 4) {
1844 header_size = name_offset +
1845 sizeof(struct profile_element);
1846
1847 } else {
1848 header_size =
1849 sizeof(struct profile_names_header);
1850 }
1851 buf_ptr = (vm_offset_t)profile_header;
1852 resid_off = 0;
1853
1854 /* write names file header */
1855 while(header_size) {
1856 error = vn_rdwr(UIO_WRITE, names_vp,
1857 (caddr_t)buf_ptr,
1858 header_size, resid_off,
1859 UIO_SYSSPACE32, IO_NODELOCKED,
1860 kauth_cred_get(), &resid, p);
1861 if(error) {
1862 printf("bsd_write_page_cache_file: Can't write header %x\n", user);
1863 bsd_close_page_cache_files(
1864 uid_files);
1865 thread_funnel_set(
1866 kernel_flock, funnel_state);
1867 return error;
1868 }
1869 buf_ptr += (vm_offset_t)header_size-resid;
1870 resid_off += header_size-resid;
1871 header_size = resid;
1872 }
1873 /* write profile to data file */
1874 resid_off = file_size;
1875 while(size) {
1876 error = vn_rdwr(UIO_WRITE, data_vp,
1877 (caddr_t)buffer, size, resid_off,
1878 UIO_SYSSPACE32, IO_NODELOCKED,
1879 kauth_cred_get(), &resid, p);
1880 if(error) {
1881 printf("bsd_write_page_cache_file: Can't write header %x\n", user);
1882 bsd_close_page_cache_files(
1883 uid_files);
1884 thread_funnel_set(
1885 kernel_flock, funnel_state);
1886 return error;
1887 }
1888 buffer += size-resid;
1889 resid_off += size-resid;
1890 size = resid;
1891 }
1892 bsd_close_page_cache_files(uid_files);
1893 thread_funnel_set(kernel_flock, funnel_state);
1894 return 0;
1895 }
1896 /* Someone else wrote a twin profile before us */
1897 bsd_close_page_cache_files(uid_files);
1898 thread_funnel_set(kernel_flock, funnel_state);
1899 return 0;
1900 } else {
1901 bsd_close_page_cache_files(uid_files);
1902 thread_funnel_set(kernel_flock, funnel_state);
1903 return EINVAL;
1904 }
1905
1906 }
1907
1908 int
1909 prepare_profile_database(int user)
1910 {
1911 const char *cache_path = "/var/vm/app_profile/";
1912 struct proc *p;
1913 int error;
1914 int resid;
1915 off_t resid_off;
1916 vm_size_t size;
1917
1918 struct vnode *names_vp;
1919 struct vnode *data_vp;
1920 vm_offset_t names_buf;
1921 vm_offset_t buf_ptr;
1922
1923 int profile_names_length;
1924 int profile_data_length;
1925 char *profile_data_string;
1926 char *profile_names_string;
1927 char *substring;
1928
1929 struct vnode_attr va;
1930 struct vfs_context context;
1931
1932 struct profile_names_header *profile_header;
1933 kern_return_t ret;
1934
1935 struct nameidata nd_names;
1936 struct nameidata nd_data;
1937
1938 p = current_proc();
1939
1940 context.vc_proc = p;
1941 context.vc_ucred = kauth_cred_get();
1942
1943 ret = kmem_alloc(kernel_map,
1944 (vm_offset_t *)&profile_data_string, PATH_MAX);
1945
1946 if(ret) {
1947 return ENOMEM;
1948 }
1949
1950 /* Split the buffer in half since we know the size of */
1951 /* our file path and our allocation is adequate for */
1952 /* both file path names */
1953 profile_names_string = profile_data_string + (PATH_MAX/2);
1954
1955
1956 strcpy(profile_data_string, cache_path);
1957 strcpy(profile_names_string, cache_path);
1958 profile_names_length = profile_data_length
1959 = strlen(profile_data_string);
1960 substring = profile_data_string + profile_data_length;
1961 sprintf(substring, "%x_data", user);
1962 substring = profile_names_string + profile_names_length;
1963 sprintf(substring, "%x_names", user);
1964
1965 /* We now have the absolute file names */
1966
1967 ret = kmem_alloc(kernel_map,
1968 (vm_offset_t *)&names_buf, 4 * PAGE_SIZE);
1969 if(ret) {
1970 kmem_free(kernel_map,
1971 (vm_offset_t)profile_data_string, PATH_MAX);
1972 return ENOMEM;
1973 }
1974
1975 NDINIT(&nd_names, LOOKUP, FOLLOW,
1976 UIO_SYSSPACE32, CAST_USER_ADDR_T(profile_names_string), &context);
1977 NDINIT(&nd_data, LOOKUP, FOLLOW,
1978 UIO_SYSSPACE32, CAST_USER_ADDR_T(profile_data_string), &context);
1979
1980 if ( (error = vn_open(&nd_data,
1981 O_CREAT | O_EXCL | FWRITE, S_IRUSR|S_IWUSR)) ) {
1982 kmem_free(kernel_map,
1983 (vm_offset_t)names_buf, 4 * PAGE_SIZE);
1984 kmem_free(kernel_map,
1985 (vm_offset_t)profile_data_string, PATH_MAX);
1986
1987 return 0;
1988 }
1989 data_vp = nd_data.ni_vp;
1990
1991 if ( (error = vn_open(&nd_names,
1992 O_CREAT | O_EXCL | FWRITE, S_IRUSR|S_IWUSR)) ) {
1993 printf("prepare_profile_database: Can't create CacheNames %s\n",
1994 profile_data_string);
1995 kmem_free(kernel_map,
1996 (vm_offset_t)names_buf, 4 * PAGE_SIZE);
1997 kmem_free(kernel_map,
1998 (vm_offset_t)profile_data_string, PATH_MAX);
1999
2000 vnode_rele(data_vp);
2001 vnode_put(data_vp);
2002
2003 return error;
2004 }
2005 names_vp = nd_names.ni_vp;
2006
2007 /* Write Header for new names file */
2008
2009 profile_header = (struct profile_names_header *)names_buf;
2010
2011 profile_header->number_of_profiles = 0;
2012 profile_header->user_id = user;
2013 profile_header->version = 1;
2014 profile_header->element_array =
2015 sizeof(struct profile_names_header);
2016 profile_header->spare1 = 0;
2017 profile_header->spare2 = 0;
2018 profile_header->spare3 = 0;
2019
2020 size = sizeof(struct profile_names_header);
2021 buf_ptr = (vm_offset_t)profile_header;
2022 resid_off = 0;
2023
2024 while(size) {
2025 error = vn_rdwr(UIO_WRITE, names_vp,
2026 (caddr_t)buf_ptr, size, resid_off,
2027 UIO_SYSSPACE32, IO_NODELOCKED,
2028 kauth_cred_get(), &resid, p);
2029 if(error) {
2030 printf("prepare_profile_database: Can't write header %s\n", profile_names_string);
2031 kmem_free(kernel_map,
2032 (vm_offset_t)names_buf, 4 * PAGE_SIZE);
2033 kmem_free(kernel_map,
2034 (vm_offset_t)profile_data_string,
2035 PATH_MAX);
2036
2037 vnode_rele(names_vp);
2038 vnode_put(names_vp);
2039 vnode_rele(data_vp);
2040 vnode_put(data_vp);
2041
2042 return error;
2043 }
2044 buf_ptr += size-resid;
2045 resid_off += size-resid;
2046 size = resid;
2047 }
2048 VATTR_INIT(&va);
2049 VATTR_SET(&va, va_uid, user);
2050
2051 error = vnode_setattr(names_vp, &va, &context);
2052 if(error) {
2053 printf("prepare_profile_database: "
2054 "Can't set user %s\n", profile_names_string);
2055 }
2056 vnode_rele(names_vp);
2057 vnode_put(names_vp);
2058
2059 VATTR_INIT(&va);
2060 VATTR_SET(&va, va_uid, user);
2061 error = vnode_setattr(data_vp, &va, &context);
2062 if(error) {
2063 printf("prepare_profile_database: "
2064 "Can't set user %s\n", profile_data_string);
2065 }
2066 vnode_rele(data_vp);
2067 vnode_put(data_vp);
2068
2069 kmem_free(kernel_map,
2070 (vm_offset_t)profile_data_string, PATH_MAX);
2071 kmem_free(kernel_map,
2072 (vm_offset_t)names_buf, 4 * PAGE_SIZE);
2073 return 0;
2074
2075 }