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