/* * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * Mach Operating System * Copyright (c) 1987 Carnegie-Mellon University * All rights reserved. The CMU software License Agreement specifies * the terms and conditions for use and redistribution. */ /* */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern shared_region_mapping_t system_shared_region; extern zone_t lsf_zone; useracc(addr, len, prot) caddr_t addr; u_int len; int prot; { return (vm_map_check_protection( current_map(), trunc_page(addr), round_page(addr+len), prot == B_READ ? VM_PROT_READ : VM_PROT_WRITE)); } vslock(addr, len) caddr_t addr; int len; { kern_return_t kret; kret = vm_map_wire(current_map(), trunc_page(addr), round_page(addr+len), VM_PROT_READ | VM_PROT_WRITE ,FALSE); switch (kret) { case KERN_SUCCESS: return (0); case KERN_INVALID_ADDRESS: case KERN_NO_SPACE: return (ENOMEM); case KERN_PROTECTION_FAILURE: return (EACCES); default: return (EINVAL); } } vsunlock(addr, len, dirtied) caddr_t addr; int len; int dirtied; { pmap_t pmap; #if FIXME /* [ */ vm_page_t pg; #endif /* FIXME ] */ vm_offset_t vaddr, paddr; kern_return_t kret; #if FIXME /* [ */ if (dirtied) { pmap = get_task_pmap(current_task()); for (vaddr = trunc_page(addr); vaddr < round_page(addr+len); vaddr += PAGE_SIZE) { paddr = pmap_extract(pmap, vaddr); pg = PHYS_TO_VM_PAGE(paddr); vm_page_set_modified(pg); } } #endif /* FIXME ] */ #ifdef lint dirtied++; #endif /* lint */ kret = vm_map_unwire(current_map(), trunc_page(addr), round_page(addr+len), FALSE); switch (kret) { case KERN_SUCCESS: return (0); case KERN_INVALID_ADDRESS: case KERN_NO_SPACE: return (ENOMEM); case KERN_PROTECTION_FAILURE: return (EACCES); default: return (EINVAL); } } #if defined(sun) || BALANCE || defined(m88k) #else /*defined(sun) || BALANCE || defined(m88k)*/ subyte(addr, byte) void * addr; int byte; { char character; character = (char)byte; return (copyout((void *)&(character), addr, sizeof(char)) == 0 ? 0 : -1); } suibyte(addr, byte) void * addr; int byte; { char character; character = (char)byte; return (copyout((void *) &(character), addr, sizeof(char)) == 0 ? 0 : -1); } int fubyte(addr) void * addr; { unsigned char byte; if (copyin(addr, (void *) &byte, sizeof(char))) return(-1); return(byte); } int fuibyte(addr) void * addr; { unsigned char byte; if (copyin(addr, (void *) &(byte), sizeof(char))) return(-1); return(byte); } suword(addr, word) void * addr; long word; { return (copyout((void *) &word, addr, sizeof(int)) == 0 ? 0 : -1); } long fuword(addr) void * addr; { long word; if (copyin(addr, (void *) &word, sizeof(int))) return(-1); return(word); } /* suiword and fuiword are the same as suword and fuword, respectively */ suiword(addr, word) void * addr; long word; { return (copyout((void *) &word, addr, sizeof(int)) == 0 ? 0 : -1); } long fuiword(addr) void * addr; { long word; if (copyin(addr, (void *) &word, sizeof(int))) return(-1); return(word); } #endif /* defined(sun) || BALANCE || defined(m88k) || defined(i386) */ int swapon() { return(EOPNOTSUPP); } kern_return_t pid_for_task(t, x) mach_port_t t; int *x; { struct proc * p; task_t t1; extern task_t port_name_to_task(mach_port_t t); int pid = -1; kern_return_t err = KERN_SUCCESS; boolean_t funnel_state; funnel_state = thread_funnel_set(kernel_flock, TRUE); t1 = port_name_to_task(t); if (t1 == TASK_NULL) { err = KERN_FAILURE; goto pftout; } else { p = get_bsdtask_info(t1); if (p) { pid = p->p_pid; err = KERN_SUCCESS; } else { err = KERN_FAILURE; } } task_deallocate(t1); pftout: (void) copyout((char *) &pid, (char *) x, sizeof(*x)); thread_funnel_set(kernel_flock, funnel_state); return(err); } /* * Routine: task_for_pid * Purpose: * Get the task port for another "process", named by its * process ID on the same host as "target_task". * * Only permitted to privileged processes, or processes * with the same user ID. */ kern_return_t task_for_pid(target_tport, pid, t) mach_port_t target_tport; int pid; mach_port_t *t; { struct proc *p; struct proc *p1; task_t t1; mach_port_t tret; extern task_t port_name_to_task(mach_port_t tp); void * sright; int error = 0; boolean_t funnel_state; t1 = port_name_to_task(target_tport); if (t1 == TASK_NULL) { (void ) copyout((char *)&t1, (char *)t, sizeof(mach_port_t)); return(KERN_FAILURE); } funnel_state = thread_funnel_set(kernel_flock, TRUE); restart: p1 = get_bsdtask_info(t1); if ( ((p = pfind(pid)) != (struct proc *) 0) && (p1 != (struct proc *) 0) && (((p->p_ucred->cr_uid == p1->p_ucred->cr_uid) && ((p->p_cred->p_ruid == p1->p_cred->p_ruid))) || !(suser(p1->p_ucred, &p1->p_acflag))) && (p->p_stat != SZOMB) ) { if (p->task != TASK_NULL) { if (!task_reference_try(p->task)) { mutex_pause(); /* temp loss of funnel */ goto restart; } sright = (void *)convert_task_to_port(p->task); tret = (void *) ipc_port_copyout_send(sright, get_task_ipcspace(current_task())); } else tret = MACH_PORT_NULL; (void ) copyout((char *)&tret, (char *) t, sizeof(mach_port_t)); task_deallocate(t1); error = KERN_SUCCESS; goto tfpout; } task_deallocate(t1); tret = MACH_PORT_NULL; (void) copyout((char *) &tret, (char *) t, sizeof(mach_port_t)); error = KERN_FAILURE; tfpout: thread_funnel_set(kernel_flock, funnel_state); return(error); } struct load_shared_file_args { char *filename; caddr_t mfa; u_long mfs; caddr_t *ba; int map_cnt; sf_mapping_t *mappings; int *flags; }; int ws_disabled = 1; int load_shared_file( struct proc *p, struct load_shared_file_args *uap, register *retval) { caddr_t mapped_file_addr=uap->mfa; u_long mapped_file_size=uap->mfs; caddr_t *base_address=uap->ba; int map_cnt=uap->map_cnt; sf_mapping_t *mappings=uap->mappings; char *filename=uap->filename; int *flags=uap->flags; struct vnode *vp = 0; struct nameidata nd, *ndp; char *filename_str; register int error; kern_return_t kr; struct vattr vattr; memory_object_control_t file_control; sf_mapping_t *map_list; caddr_t local_base; int local_flags; int caller_flags; int i; int default_regions = 0; vm_size_t dummy; kern_return_t kret; shared_region_mapping_t shared_region; struct shared_region_task_mappings task_mapping_info; shared_region_mapping_t next; ndp = &nd; /* Retrieve the base address */ if (error = copyin(base_address, &local_base, sizeof (caddr_t))) { goto lsf_bailout; } if (error = copyin(flags, &local_flags, sizeof (int))) { goto lsf_bailout; } if(local_flags & QUERY_IS_SYSTEM_REGION) { vm_get_shared_region(current_task(), &shared_region); if (shared_region == system_shared_region) { local_flags = SYSTEM_REGION_BACKED; } else { local_flags = 0; } error = 0; error = copyout(&local_flags, flags, sizeof (int)); goto lsf_bailout; } caller_flags = local_flags; kret = kmem_alloc(kernel_map, (vm_offset_t *)&filename_str, (vm_size_t)(MAXPATHLEN)); if (kret != KERN_SUCCESS) { error = ENOMEM; goto lsf_bailout; } kret = kmem_alloc(kernel_map, (vm_offset_t *)&map_list, (vm_size_t)(map_cnt*sizeof(sf_mapping_t))); if (kret != KERN_SUCCESS) { kmem_free(kernel_map, (vm_offset_t)filename_str, (vm_size_t)(MAXPATHLEN)); error = ENOMEM; goto lsf_bailout; } if (error = copyin(mappings, map_list, (map_cnt*sizeof(sf_mapping_t)))) { goto lsf_bailout_free; } if (error = copyinstr(filename, filename_str, MAXPATHLEN, (size_t *)&dummy)) { goto lsf_bailout_free; } /* * Get a vnode for the target file */ NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, filename_str, p); if ((error = namei(ndp))) { goto lsf_bailout_free; } vp = ndp->ni_vp; if (vp->v_type != VREG) { error = EINVAL; goto lsf_bailout_free_vput; } UBCINFOCHECK("load_shared_file", vp); if (error = VOP_GETATTR(vp, &vattr, p->p_ucred, p)) { goto lsf_bailout_free_vput; } file_control = ubc_getobject(vp, UBC_HOLDOBJECT); if (file_control == MEMORY_OBJECT_CONTROL_NULL) { error = EINVAL; goto lsf_bailout_free_vput; } vm_get_shared_region(current_task(), &shared_region); if(shared_region == system_shared_region) { default_regions = 1; } if(((vp->v_mount != rootvnode->v_mount) && (shared_region == system_shared_region)) && (lsf_mapping_pool_gauge() < 75)) { /* We don't want to run out of shared memory */ /* map entries by starting too many private versions */ /* of the shared library structures */ int error; if(p->p_flag & P_NOSHLIB) { error = clone_system_shared_regions(FALSE); } else { error = clone_system_shared_regions(TRUE); } if (error) { goto lsf_bailout_free_vput; } local_flags = local_flags & ~NEW_LOCAL_SHARED_REGIONS; vm_get_shared_region(current_task(), &shared_region); } #ifdef notdef if(vattr.va_size != mapped_file_size) { error = EINVAL; goto lsf_bailout_free_vput; } #endif if(p->p_flag & P_NOSHLIB) { p->p_flag = p->p_flag & ~P_NOSHLIB; } /* load alternate regions if the caller has requested. */ /* Note: the new regions are "clean slates" */ if (local_flags & NEW_LOCAL_SHARED_REGIONS) { error = clone_system_shared_regions(FALSE); if (error) { goto lsf_bailout_free_vput; } vm_get_shared_region(current_task(), &shared_region); } task_mapping_info.self = (vm_offset_t)shared_region; shared_region_mapping_info(shared_region, &(task_mapping_info.text_region), &(task_mapping_info.text_size), &(task_mapping_info.data_region), &(task_mapping_info.data_size), &(task_mapping_info.region_mappings), &(task_mapping_info.client_base), &(task_mapping_info.alternate_base), &(task_mapping_info.alternate_next), &(task_mapping_info.flags), &next); /* This is a work-around to allow executables which have been */ /* built without knowledge of the proper shared segment to */ /* load. This code has been architected as a shared region */ /* handler, the knowledge of where the regions are loaded is */ /* problematic for the extension of shared regions as it will */ /* not be easy to know what region an item should go into. */ /* The code below however will get around a short term problem */ /* with executables which believe they are loading at zero. */ { if (((unsigned int)local_base & (~(task_mapping_info.text_size - 1))) != task_mapping_info.client_base) { if(local_flags & ALTERNATE_LOAD_SITE) { local_base = (caddr_t)( (unsigned int)local_base & (task_mapping_info.text_size - 1)); local_base = (caddr_t)((unsigned int)local_base | task_mapping_info.client_base); } else { error = EINVAL; goto lsf_bailout_free_vput; } } } if((kr = copyin_shared_file((vm_offset_t)mapped_file_addr, mapped_file_size, (vm_offset_t *)&local_base, map_cnt, map_list, file_control, &task_mapping_info, &local_flags))) { switch (kr) { case KERN_FAILURE: error = EINVAL; break; case KERN_INVALID_ARGUMENT: error = EINVAL; break; case KERN_INVALID_ADDRESS: error = EACCES; break; case KERN_PROTECTION_FAILURE: /* save EAUTH for authentication in this */ /* routine */ error = EPERM; break; case KERN_NO_SPACE: error = ENOMEM; break; default: error = EINVAL; }; if((caller_flags & ALTERNATE_LOAD_SITE) && systemLogDiags) { printf("load_shared_file: Failed to load shared file! error: 0x%x, Base_address: 0x%x, number of mappings: %d, file_control 0x%x\n", error, local_base, map_cnt, file_control); for(i=0; iba; int map_cnt=uap->map_cnt; sf_mapping_t *mappings=uap->mappings; register int error; kern_return_t kr; sf_mapping_t *map_list; caddr_t local_base; vm_offset_t map_address; int i; kern_return_t kret; /* Retrieve the base address */ if (error = copyin(base_address, &local_base, sizeof (caddr_t))) { goto rsf_bailout; } if (((unsigned int)local_base & GLOBAL_SHARED_SEGMENT_MASK) != GLOBAL_SHARED_TEXT_SEGMENT) { error = EINVAL; goto rsf_bailout; } kret = kmem_alloc(kernel_map, (vm_offset_t *)&map_list, (vm_size_t)(map_cnt*sizeof(sf_mapping_t))); if (kret != KERN_SUCCESS) { error = ENOMEM; goto rsf_bailout; } if (error = copyin(mappings, map_list, (map_cnt*sizeof(sf_mapping_t)))) { kmem_free(kernel_map, (vm_offset_t)map_list, (vm_size_t)(map_cnt*sizeof(sf_mapping_t))); goto rsf_bailout; } for (i = 0; ibusy) { /* * drop funnel and wait */ (void)tsleep((void *) *profile, PRIBIO, "app_profile", 0); goto restart; } (*profile)->busy = 1; (*profile)->age = global_user_profile_cache.age; global_user_profile_cache.age+=1; return 0; } } lru = global_user_profile_cache.age; for(i = 0; iage = global_user_profile_cache.age; global_user_profile_cache.age+=1; break; } if(global_user_profile_cache.profiles[i].age < lru) { lru = global_user_profile_cache.profiles[i].age; *profile = &global_user_profile_cache.profiles[i]; } } if ((*profile)->busy) { /* * drop funnel and wait */ (void)tsleep((void *) &(global_user_profile_cache), PRIBIO, "app_profile", 0); goto restart; } (*profile)->busy = 1; (*profile)->user = user; if((*profile)->data_vp != NULL) { kmem_free(kernel_map, (*profile)->buf_ptr, 4 * PAGE_SIZE); if ((*profile)->names_vp) { vrele((*profile)->names_vp); (*profile)->names_vp = NULL; } if ((*profile)->data_vp) { vrele((*profile)->data_vp); (*profile)->data_vp = NULL; } } /* put dummy value in for now to get */ /* competing request to wait above */ /* until we are finished */ (*profile)->data_vp = (struct vnode *)0xFFFFFFFF; /* Try to open the appropriate users profile files */ /* If neither file is present, try to create them */ /* If one file is present and the other not, fail. */ /* If the files do exist, check them for the app_file */ /* requested and read it in if present */ ret = kmem_alloc(kernel_map, (vm_offset_t *)&profile_data_string, PATH_MAX); if(ret) { (*profile)->data_vp = NULL; (*profile)->busy = 0; wakeup(*profile); return ENOMEM; } /* Split the buffer in half since we know the size of */ /* our file path and our allocation is adequate for */ /* both file path names */ profile_names_string = profile_data_string + (PATH_MAX/2); strcpy(profile_data_string, cache_path); strcpy(profile_names_string, cache_path); profile_names_length = profile_data_length = strlen(profile_data_string); substring = profile_data_string + profile_data_length; sprintf(substring, "%x_data", user); substring = profile_names_string + profile_names_length; sprintf(substring, "%x_names", user); /* We now have the absolute file names */ ret = kmem_alloc(kernel_map, (vm_offset_t *)&names_buf, 4 * PAGE_SIZE); if(ret) { kmem_free(kernel_map, (vm_offset_t)profile_data_string, PATH_MAX); (*profile)->data_vp = NULL; (*profile)->busy = 0; wakeup(*profile); return ENOMEM; } NDINIT(&nd_names, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, profile_names_string, p); NDINIT(&nd_data, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, profile_data_string, p); if (error = vn_open(&nd_data, FREAD | FWRITE, 0)) { #ifdef notdef printf("bsd_open_page_cache_files: CacheData file not found %s\n", profile_data_string); #endif kmem_free(kernel_map, (vm_offset_t)names_buf, 4 * PAGE_SIZE); kmem_free(kernel_map, (vm_offset_t)profile_data_string, PATH_MAX); (*profile)->data_vp = NULL; (*profile)->busy = 0; wakeup(*profile); return error; } data_vp = nd_data.ni_vp; VOP_UNLOCK(data_vp, 0, p); if (error = vn_open(&nd_names, FREAD | FWRITE, 0)) { printf("bsd_open_page_cache_files: NamesData file not found %s\n", profile_data_string); kmem_free(kernel_map, (vm_offset_t)names_buf, 4 * PAGE_SIZE); kmem_free(kernel_map, (vm_offset_t)profile_data_string, PATH_MAX); vrele(data_vp); (*profile)->data_vp = NULL; (*profile)->busy = 0; wakeup(*profile); return error; } names_vp = nd_names.ni_vp; if(error = VOP_GETATTR(names_vp, &vattr, p->p_ucred, p)) { printf("bsd_open_page_cache_files: Can't stat name file %s\n", profile_names_string); kmem_free(kernel_map, (vm_offset_t)profile_data_string, PATH_MAX); kmem_free(kernel_map, (vm_offset_t)names_buf, 4 * PAGE_SIZE); vput(names_vp); vrele(data_vp); (*profile)->data_vp = NULL; (*profile)->busy = 0; wakeup(*profile); return error; } size = vattr.va_size; if(size > 4 * PAGE_SIZE) size = 4 * PAGE_SIZE; buf_ptr = names_buf; resid_off = 0; while(size) { error = vn_rdwr(UIO_READ, names_vp, (caddr_t)buf_ptr, size, resid_off, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p); if((error) || (size == resid)) { if(!error) { error = EINVAL; } kmem_free(kernel_map, (vm_offset_t)profile_data_string, PATH_MAX); kmem_free(kernel_map, (vm_offset_t)names_buf, 4 * PAGE_SIZE); vput(names_vp); vrele(data_vp); (*profile)->data_vp = NULL; (*profile)->busy = 0; wakeup(*profile); return error; } buf_ptr += size-resid; resid_off += size-resid; size = resid; } VOP_UNLOCK(names_vp, 0, p); kmem_free(kernel_map, (vm_offset_t)profile_data_string, PATH_MAX); (*profile)->names_vp = names_vp; (*profile)->data_vp = data_vp; (*profile)->buf_ptr = names_buf; return 0; } void bsd_close_page_cache_files( struct global_profile *profile) { profile->busy = 0; wakeup(profile); } int bsd_read_page_cache_file( unsigned int user, int *fid, int *mod, char *app_name, struct vnode *app_vp, vm_offset_t *buffer, vm_offset_t *buf_size) { boolean_t funnel_state; struct proc *p; int error; int resid; vm_size_t size; off_t profile; unsigned int profile_size; vm_offset_t names_buf; struct vattr vattr; kern_return_t ret; struct vnode *names_vp; struct vnode *data_vp; struct vnode *vp1; struct vnode *vp2; struct global_profile *uid_files; funnel_state = thread_funnel_set(kernel_flock, TRUE); /* Try to open the appropriate users profile files */ /* If neither file is present, try to create them */ /* If one file is present and the other not, fail. */ /* If the files do exist, check them for the app_file */ /* requested and read it in if present */ error = bsd_open_page_cache_files(user, &uid_files); if(error) { thread_funnel_set(kernel_flock, funnel_state); return EINVAL; } p = current_proc(); names_vp = uid_files->names_vp; data_vp = uid_files->data_vp; names_buf = uid_files->buf_ptr; /* * Get locks on both files, get the vnode with the lowest address first */ if((unsigned int)names_vp < (unsigned int)data_vp) { vp1 = names_vp; vp2 = data_vp; } else { vp1 = data_vp; vp2 = names_vp; } error = vn_lock(vp1, LK_EXCLUSIVE | LK_RETRY, p); if(error) { printf("bsd_read_page_cache_file: Can't lock profile names %x\n", user); bsd_close_page_cache_files(uid_files); thread_funnel_set(kernel_flock, funnel_state); return error; } error = vn_lock(vp2, LK_EXCLUSIVE | LK_RETRY, p); if(error) { printf("bsd_read_page_cache_file: Can't lock profile data %x\n", user); VOP_UNLOCK(vp1, 0, p); bsd_close_page_cache_files(uid_files); thread_funnel_set(kernel_flock, funnel_state); return error; } if(error = VOP_GETATTR(app_vp, &vattr, p->p_ucred, p)) { VOP_UNLOCK(names_vp, 0, p); VOP_UNLOCK(data_vp, 0, p); printf("bsd_read_cache_file: Can't stat app file %s\n", app_name); bsd_close_page_cache_files(uid_files); thread_funnel_set(kernel_flock, funnel_state); return error; } *fid = vattr.va_fileid; *mod = vattr.va_mtime.tv_sec; if (bsd_search_page_cache_data_base(names_vp, names_buf, app_name, (unsigned int) vattr.va_mtime.tv_sec, vattr.va_fileid, &profile, &profile_size) == 0) { /* profile is an offset in the profile data base */ /* It is zero if no profile data was found */ if(profile_size == 0) { *buffer = NULL; *buf_size = 0; VOP_UNLOCK(names_vp, 0, p); VOP_UNLOCK(data_vp, 0, p); bsd_close_page_cache_files(uid_files); thread_funnel_set(kernel_flock, funnel_state); return 0; } ret = (vm_offset_t)(kmem_alloc(kernel_map, buffer, profile_size)); if(ret) { VOP_UNLOCK(names_vp, 0, p); VOP_UNLOCK(data_vp, 0, p); bsd_close_page_cache_files(uid_files); thread_funnel_set(kernel_flock, funnel_state); return ENOMEM; } *buf_size = profile_size; while(profile_size) { error = vn_rdwr(UIO_READ, data_vp, (caddr_t) *buffer, profile_size, profile, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p); if((error) || (profile_size == resid)) { VOP_UNLOCK(names_vp, 0, p); VOP_UNLOCK(data_vp, 0, p); bsd_close_page_cache_files(uid_files); kmem_free(kernel_map, (vm_offset_t)*buffer, profile_size); thread_funnel_set(kernel_flock, funnel_state); return EINVAL; } profile += profile_size - resid; profile_size = resid; } VOP_UNLOCK(names_vp, 0, p); VOP_UNLOCK(data_vp, 0, p); bsd_close_page_cache_files(uid_files); thread_funnel_set(kernel_flock, funnel_state); return 0; } else { VOP_UNLOCK(names_vp, 0, p); VOP_UNLOCK(data_vp, 0, p); bsd_close_page_cache_files(uid_files); thread_funnel_set(kernel_flock, funnel_state); return EINVAL; } } int bsd_search_page_cache_data_base( struct vnode *vp, struct profile_names_header *database, char *app_name, unsigned int mod_date, unsigned int inode, off_t *profile, unsigned int *profile_size) { struct proc *p; unsigned int i; struct profile_element *element; unsigned int ele_total; unsigned int extended_list = 0; off_t file_off = 0; unsigned int size; off_t resid_off; int resid; vm_offset_t local_buf = NULL; int error; kern_return_t ret; p = current_proc(); if(((vm_offset_t)database->element_array) != sizeof(struct profile_names_header)) { return EINVAL; } element = (struct profile_element *)( (vm_offset_t)database->element_array + (vm_offset_t)database); ele_total = database->number_of_profiles; *profile = 0; *profile_size = 0; while(ele_total) { /* note: code assumes header + n*ele comes out on a page boundary */ if(((local_buf == 0) && (sizeof(struct profile_names_header) + (ele_total * sizeof(struct profile_element))) > (PAGE_SIZE * 4)) || ((local_buf != 0) && (ele_total * sizeof(struct profile_element)) > (PAGE_SIZE * 4))) { extended_list = ele_total; if(element == (struct profile_element *) ((vm_offset_t)database->element_array + (vm_offset_t)database)) { ele_total = ((PAGE_SIZE * 4)/sizeof(struct profile_element)) - 1; } else { ele_total = (PAGE_SIZE * 4)/sizeof(struct profile_element); } extended_list -= ele_total; } for (i=0; i (PAGE_SIZE * 4)) { size = PAGE_SIZE * 4; } else { size = ele_total * sizeof(struct profile_element); } resid_off = 0; while(size) { error = vn_rdwr(UIO_READ, vp, (caddr_t)(local_buf + resid_off), size, file_off + resid_off, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p); if((error) || (size == resid)) { if(local_buf != NULL) { kmem_free(kernel_map, (vm_offset_t)local_buf, 4 * PAGE_SIZE); } return EINVAL; } resid_off += size-resid; size = resid; } } if(local_buf != NULL) { kmem_free(kernel_map, (vm_offset_t)local_buf, 4 * PAGE_SIZE); } return 0; } int bsd_write_page_cache_file( unsigned int user, char *file_name, caddr_t buffer, vm_size_t size, int mod, int fid) { struct proc *p; struct nameidata nd; struct vnode *vp = 0; int resid; off_t resid_off; int error; boolean_t funnel_state; struct vattr vattr; struct vattr data_vattr; off_t profile; unsigned int profile_size; vm_offset_t names_buf; struct vnode *names_vp; struct vnode *data_vp; struct vnode *vp1; struct vnode *vp2; struct profile_names_header *profile_header; off_t name_offset; struct global_profile *uid_files; funnel_state = thread_funnel_set(kernel_flock, TRUE); error = bsd_open_page_cache_files(user, &uid_files); if(error) { thread_funnel_set(kernel_flock, funnel_state); return EINVAL; } p = current_proc(); names_vp = uid_files->names_vp; data_vp = uid_files->data_vp; names_buf = uid_files->buf_ptr; /* * Get locks on both files, get the vnode with the lowest address first */ if((unsigned int)names_vp < (unsigned int)data_vp) { vp1 = names_vp; vp2 = data_vp; } else { vp1 = data_vp; vp2 = names_vp; } error = vn_lock(vp1, LK_EXCLUSIVE | LK_RETRY, p); if(error) { printf("bsd_write_page_cache_file: Can't lock profile names %x\n", user); bsd_close_page_cache_files(uid_files); thread_funnel_set(kernel_flock, funnel_state); return error; } error = vn_lock(vp2, LK_EXCLUSIVE | LK_RETRY, p); if(error) { printf("bsd_write_page_cache_file: Can't lock profile data %x\n", user); VOP_UNLOCK(vp1, 0, p); bsd_close_page_cache_files(uid_files); thread_funnel_set(kernel_flock, funnel_state); return error; } /* Stat data file for size */ if(error = VOP_GETATTR(data_vp, &data_vattr, p->p_ucred, p)) { VOP_UNLOCK(names_vp, 0, p); VOP_UNLOCK(data_vp, 0, p); printf("bsd_write_page_cache_file: Can't stat profile data %s\n", file_name); bsd_close_page_cache_files(uid_files); thread_funnel_set(kernel_flock, funnel_state); return error; } if (bsd_search_page_cache_data_base(names_vp, (struct profile_names_header *)names_buf, file_name, (unsigned int) mod, fid, &profile, &profile_size) == 0) { /* profile is an offset in the profile data base */ /* It is zero if no profile data was found */ if(profile_size == 0) { unsigned int header_size; vm_offset_t buf_ptr; /* Our Write case */ /* read header for last entry */ profile_header = (struct profile_names_header *)names_buf; name_offset = sizeof(struct profile_names_header) + (sizeof(struct profile_element) * profile_header->number_of_profiles); profile_header->number_of_profiles += 1; if(name_offset < PAGE_SIZE * 4) { struct profile_element *name; /* write new entry */ name = (struct profile_element *) (names_buf + (vm_offset_t)name_offset); name->addr = data_vattr.va_size; name->size = size; name->mod_date = mod; name->inode = fid; strncpy (name->name, file_name, 12); } else { unsigned int ele_size; struct profile_element name; /* write new entry */ name.addr = data_vattr.va_size; name.size = size; name.mod_date = mod; name.inode = fid; strncpy (name.name, file_name, 12); /* write element out separately */ ele_size = sizeof(struct profile_element); buf_ptr = (vm_offset_t)&name; resid_off = name_offset; while(ele_size) { error = vn_rdwr(UIO_WRITE, names_vp, (caddr_t)buf_ptr, ele_size, resid_off, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p); if(error) { printf("bsd_write_page_cache_file: Can't write name_element %x\n", user); VOP_UNLOCK(names_vp, 0, p); VOP_UNLOCK(data_vp, 0, p); bsd_close_page_cache_files( uid_files); thread_funnel_set( kernel_flock, funnel_state); return error; } buf_ptr += (vm_offset_t) ele_size-resid; resid_off += ele_size-resid; ele_size = resid; } } if(name_offset < PAGE_SIZE * 4) { header_size = name_offset + sizeof(struct profile_element); } else { header_size = sizeof(struct profile_names_header); } buf_ptr = (vm_offset_t)profile_header; resid_off = 0; /* write names file header */ while(header_size) { error = vn_rdwr(UIO_WRITE, names_vp, (caddr_t)buf_ptr, header_size, resid_off, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p); if(error) { VOP_UNLOCK(names_vp, 0, p); VOP_UNLOCK(data_vp, 0, p); printf("bsd_write_page_cache_file: Can't write header %x\n", user); bsd_close_page_cache_files( uid_files); thread_funnel_set( kernel_flock, funnel_state); return error; } buf_ptr += (vm_offset_t)header_size-resid; resid_off += header_size-resid; header_size = resid; } /* write profile to data file */ resid_off = data_vattr.va_size; while(size) { error = vn_rdwr(UIO_WRITE, data_vp, (caddr_t)buffer, size, resid_off, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p); if(error) { VOP_UNLOCK(names_vp, 0, p); VOP_UNLOCK(data_vp, 0, p); printf("bsd_write_page_cache_file: Can't write header %x\n", user); bsd_close_page_cache_files( uid_files); thread_funnel_set( kernel_flock, funnel_state); return error; } buffer += size-resid; resid_off += size-resid; size = resid; } VOP_UNLOCK(names_vp, 0, p); VOP_UNLOCK(data_vp, 0, p); bsd_close_page_cache_files(uid_files); thread_funnel_set(kernel_flock, funnel_state); return 0; } /* Someone else wrote a twin profile before us */ VOP_UNLOCK(names_vp, 0, p); VOP_UNLOCK(data_vp, 0, p); bsd_close_page_cache_files(uid_files); thread_funnel_set(kernel_flock, funnel_state); return 0; } else { VOP_UNLOCK(names_vp, 0, p); VOP_UNLOCK(data_vp, 0, p); bsd_close_page_cache_files(uid_files); thread_funnel_set(kernel_flock, funnel_state); return EINVAL; } } int prepare_profile_database(int user) { char *cache_path = "/var/vm/app_profile/"; struct proc *p; int error; int resid; off_t resid_off; unsigned int lru; vm_size_t size; struct vnode *names_vp; struct vnode *data_vp; vm_offset_t names_buf; vm_offset_t buf_ptr; int profile_names_length; int profile_data_length; char *profile_data_string; char *profile_names_string; char *substring; struct vattr vattr; struct profile_names_header *profile_header; kern_return_t ret; struct nameidata nd_names; struct nameidata nd_data; int i; p = current_proc(); ret = kmem_alloc(kernel_map, (vm_offset_t *)&profile_data_string, PATH_MAX); if(ret) { return ENOMEM; } /* Split the buffer in half since we know the size of */ /* our file path and our allocation is adequate for */ /* both file path names */ profile_names_string = profile_data_string + (PATH_MAX/2); strcpy(profile_data_string, cache_path); strcpy(profile_names_string, cache_path); profile_names_length = profile_data_length = strlen(profile_data_string); substring = profile_data_string + profile_data_length; sprintf(substring, "%x_data", user); substring = profile_names_string + profile_names_length; sprintf(substring, "%x_names", user); /* We now have the absolute file names */ ret = kmem_alloc(kernel_map, (vm_offset_t *)&names_buf, 4 * PAGE_SIZE); if(ret) { kmem_free(kernel_map, (vm_offset_t)profile_data_string, PATH_MAX); return ENOMEM; } NDINIT(&nd_names, LOOKUP, FOLLOW, UIO_SYSSPACE, profile_names_string, p); NDINIT(&nd_data, LOOKUP, FOLLOW, UIO_SYSSPACE, profile_data_string, p); if (error = vn_open(&nd_data, O_CREAT | O_EXCL | FWRITE, S_IRUSR|S_IWUSR)) { kmem_free(kernel_map, (vm_offset_t)names_buf, 4 * PAGE_SIZE); kmem_free(kernel_map, (vm_offset_t)profile_data_string, PATH_MAX); return 0; } data_vp = nd_data.ni_vp; VOP_UNLOCK(data_vp, 0, p); if (error = vn_open(&nd_names, O_CREAT | O_EXCL | FWRITE, S_IRUSR|S_IWUSR)) { printf("prepare_profile_database: Can't create CacheNames %s\n", profile_data_string); kmem_free(kernel_map, (vm_offset_t)names_buf, 4 * PAGE_SIZE); kmem_free(kernel_map, (vm_offset_t)profile_data_string, PATH_MAX); vrele(data_vp); return error; } names_vp = nd_names.ni_vp; /* Write Header for new names file */ profile_header = (struct profile_names_header *)names_buf; profile_header->number_of_profiles = 0; profile_header->user_id = user; profile_header->version = 1; profile_header->element_array = sizeof(struct profile_names_header); profile_header->spare1 = 0; profile_header->spare2 = 0; profile_header->spare3 = 0; size = sizeof(struct profile_names_header); buf_ptr = (vm_offset_t)profile_header; resid_off = 0; while(size) { error = vn_rdwr(UIO_WRITE, names_vp, (caddr_t)buf_ptr, size, resid_off, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p); if(error) { printf("prepare_profile_database: Can't write header %s\n", profile_names_string); kmem_free(kernel_map, (vm_offset_t)names_buf, 4 * PAGE_SIZE); kmem_free(kernel_map, (vm_offset_t)profile_data_string, PATH_MAX); vput(names_vp); vrele(data_vp); return error; } buf_ptr += size-resid; resid_off += size-resid; size = resid; } VATTR_NULL(&vattr); vattr.va_uid = user; error = VOP_SETATTR(names_vp, &vattr, p->p_cred->pc_ucred, p); if(error) { printf("prepare_profile_database: " "Can't set user %s\n", profile_names_string); } vput(names_vp); error = vn_lock(data_vp, LK_EXCLUSIVE | LK_RETRY, p); if(error) { vrele(data_vp); printf("prepare_profile_database: cannot lock data file %s\n", profile_data_string); kmem_free(kernel_map, (vm_offset_t)profile_data_string, PATH_MAX); kmem_free(kernel_map, (vm_offset_t)names_buf, 4 * PAGE_SIZE); } VATTR_NULL(&vattr); vattr.va_uid = user; error = VOP_SETATTR(data_vp, &vattr, p->p_cred->pc_ucred, p); if(error) { printf("prepare_profile_database: " "Can't set user %s\n", profile_data_string); } vput(data_vp); kmem_free(kernel_map, (vm_offset_t)profile_data_string, PATH_MAX); kmem_free(kernel_map, (vm_offset_t)names_buf, 4 * PAGE_SIZE); return 0; }