+
+/* header for the profile name file. The profiled app info is held */
+/* in the data file and pointed to by elements in the name file */
+
+struct profile_names_header {
+ unsigned int number_of_profiles;
+ unsigned int user_id;
+ unsigned int version;
+ off_t element_array;
+ unsigned int spare1;
+ unsigned int spare2;
+ unsigned int spare3;
+};
+
+struct profile_element {
+ off_t addr;
+ vm_size_t size;
+ unsigned int mod_date;
+ unsigned int inode;
+ char name[12];
+};
+
+struct global_profile {
+ struct vnode *names_vp;
+ struct vnode *data_vp;
+ vm_offset_t buf_ptr;
+ unsigned int user;
+ unsigned int age;
+ unsigned int busy;
+};
+
+struct global_profile_cache {
+ int max_ele;
+ unsigned int age;
+ struct global_profile profiles[3];
+};
+
+/* forward declarations */
+int bsd_open_page_cache_files(unsigned int user,
+ struct global_profile **profile);
+void bsd_close_page_cache_files(struct global_profile *profile);
+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 global_profile_cache global_user_profile_cache =
+ {3, 0, {{NULL, NULL, 0, 0, 0, 0},
+ {NULL, NULL, 0, 0, 0, 0},
+ {NULL, NULL, 0, 0, 0, 0}} };
+
+/* BSD_OPEN_PAGE_CACHE_FILES: */
+/* Caller provides a user id. This id was used in */
+/* prepare_profile_database to create two unique absolute */
+/* file paths to the associated profile files. These files */
+/* are either opened or bsd_open_page_cache_files returns an */
+/* error. The header of the names file is then consulted. */
+/* The header and the vnodes for the names and data files are */
+/* returned. */
+
+int
+bsd_open_page_cache_files(
+ unsigned int user,
+ struct global_profile **profile)
+{
+ const char *cache_path = "/var/vm/app_profile/";
+ struct proc *p;
+ int error;
+ vm_size_t 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;
+
+ off_t file_size;
+ struct vfs_context context;
+
+ kern_return_t ret;
+
+ struct nameidata nd_names;
+ struct nameidata nd_data;
+ int i;
+
+
+ p = current_proc();
+
+ context.vc_proc = p;
+ context.vc_ucred = kauth_cred_get();
+
+restart:
+ for(i = 0; i<global_user_profile_cache.max_ele; i++) {
+ if((global_user_profile_cache.profiles[i].user == user)
+ && (global_user_profile_cache.profiles[i].data_vp
+ != NULL)) {
+ *profile = &global_user_profile_cache.profiles[i];
+ /* already in cache, we're done */
+ if ((*profile)->busy) {
+ /*
+ * drop funnel and wait
+ */
+ (void)tsleep((void *)
+ *profile,
+ PRIBIO, "app_profile", 0);
+ goto restart;
+ }
+ (*profile)->busy = 1;
+ (*profile)->age = global_user_profile_cache.age;
+
+ /*
+ * entries in cache are held with a valid
+ * usecount... take an iocount which will
+ * be dropped in "bsd_close_page_cache_files"
+ * which is called after the read or writes to
+ * these files are done
+ */
+ if ( (vnode_getwithref((*profile)->data_vp)) ) {
+
+ vnode_rele((*profile)->data_vp);
+ vnode_rele((*profile)->names_vp);
+
+ (*profile)->data_vp = NULL;
+ (*profile)->busy = 0;
+ wakeup(*profile);
+
+ goto restart;
+ }
+ if ( (vnode_getwithref((*profile)->names_vp)) ) {
+
+ vnode_put((*profile)->data_vp);
+ vnode_rele((*profile)->data_vp);
+ vnode_rele((*profile)->names_vp);
+
+ (*profile)->data_vp = NULL;
+ (*profile)->busy = 0;
+ wakeup(*profile);
+
+ goto restart;
+ }
+ global_user_profile_cache.age+=1;
+ return 0;
+ }
+ }
+
+ lru = global_user_profile_cache.age;
+ *profile = NULL;
+ for(i = 0; i<global_user_profile_cache.max_ele; i++) {
+ /* Skip entry if it is in the process of being reused */
+ if(global_user_profile_cache.profiles[i].data_vp ==
+ (struct vnode *)0xFFFFFFFF)
+ continue;
+ /* Otherwise grab the first empty entry */
+ if(global_user_profile_cache.profiles[i].data_vp == NULL) {
+ *profile = &global_user_profile_cache.profiles[i];
+ (*profile)->age = global_user_profile_cache.age;
+ break;
+ }
+ /* Otherwise grab the oldest entry */
+ if(global_user_profile_cache.profiles[i].age < lru) {
+ lru = global_user_profile_cache.profiles[i].age;
+ *profile = &global_user_profile_cache.profiles[i];
+ }
+ }
+
+ /* Did we set it? */
+ if (*profile == NULL) {
+ /*
+ * No entries are available; this can only happen if all
+ * of them are currently in the process of being reused;
+ * if this happens, we sleep on the address of the first
+ * element, and restart. This is less than ideal, but we
+ * know it will work because we know that there will be a
+ * wakeup on any entry currently in the process of being
+ * reused.
+ *
+ * XXX Reccomend a two handed clock and more than 3 total
+ * XXX cache entries at some point in the future.
+ */
+ /*
+ * drop funnel and wait
+ */
+ (void)tsleep((void *)
+ &global_user_profile_cache.profiles[0],
+ PRIBIO, "app_profile", 0);
+ goto restart;
+ }
+
+ /*
+ * If it's currently busy, we've picked the one at the end of the
+ * LRU list, but it's currently being actively used. We sleep on
+ * its address and restart.
+ */
+ if ((*profile)->busy) {
+ /*
+ * drop funnel and wait
+ */
+ (void)tsleep((void *)
+ *profile,
+ PRIBIO, "app_profile", 0);
+ goto restart;
+ }
+ (*profile)->busy = 1;
+ (*profile)->user = user;
+
+ /*
+ * put dummy value in for now to get competing request to wait
+ * above until we are finished
+ *
+ * Save the data_vp before setting it, so we can set it before
+ * we kmem_free() or vrele(). If we don't do this, then we
+ * have a potential funnel race condition we have to deal with.
+ */
+ data_vp = (*profile)->data_vp;
+ (*profile)->data_vp = (struct vnode *)0xFFFFFFFF;
+
+ /*
+ * Age the cache here in all cases; this guarantees that we won't
+ * be reusing only one entry over and over, once the system reaches
+ * steady-state.
+ */
+ global_user_profile_cache.age+=1;
+
+ if(data_vp != NULL) {
+ kmem_free(kernel_map,
+ (*profile)->buf_ptr, 4 * PAGE_SIZE);
+ if ((*profile)->names_vp) {
+ vnode_rele((*profile)->names_vp);
+ (*profile)->names_vp = NULL;
+ }
+ vnode_rele(data_vp);
+ }
+
+ /* 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_SYSSPACE32, CAST_USER_ADDR_T(profile_names_string), &context);
+ NDINIT(&nd_data, LOOKUP, FOLLOW | LOCKLEAF,
+ UIO_SYSSPACE32, CAST_USER_ADDR_T(profile_data_string), &context);
+
+ 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;
+
+ 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);
+
+ vnode_rele(data_vp);
+ vnode_put(data_vp);
+
+ (*profile)->data_vp = NULL;
+ (*profile)->busy = 0;
+ wakeup(*profile);
+ return error;
+ }
+ names_vp = nd_names.ni_vp;
+
+ if ((error = vnode_size(names_vp, &file_size, &context)) != 0) {
+ 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);
+
+ vnode_rele(names_vp);
+ vnode_put(names_vp);
+ vnode_rele(data_vp);
+ vnode_put(data_vp);
+
+ (*profile)->data_vp = NULL;
+ (*profile)->busy = 0;
+ wakeup(*profile);
+ return error;
+ }
+
+ size = file_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_SYSSPACE32, IO_NODELOCKED, kauth_cred_get(), &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);
+
+ vnode_rele(names_vp);
+ vnode_put(names_vp);
+ vnode_rele(data_vp);
+ vnode_put(data_vp);
+
+ (*profile)->data_vp = NULL;
+ (*profile)->busy = 0;
+ wakeup(*profile);
+ return error;
+ }
+ buf_ptr += size-resid;
+ resid_off += size-resid;
+ size = resid;
+ }
+ 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;
+
+ /*
+ * at this point, the both the names_vp and the data_vp have
+ * both a valid usecount and an iocount held
+ */
+ return 0;
+
+}
+
+void
+bsd_close_page_cache_files(
+ struct global_profile *profile)
+{
+ vnode_put(profile->data_vp);
+ vnode_put(profile->names_vp);
+
+ 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 *bufsize)
+{
+
+ boolean_t funnel_state;
+
+ struct proc *p;
+ int error;
+ unsigned int resid;
+
+ off_t profile;
+ unsigned int profile_size;
+
+ vm_offset_t names_buf;
+ struct vnode_attr va;
+ struct vfs_context context;
+
+ kern_return_t ret;
+
+ struct vnode *names_vp;
+ struct vnode *data_vp;
+
+ 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;
+
+ context.vc_proc = p;
+ context.vc_ucred = kauth_cred_get();
+
+ VATTR_INIT(&va);
+ VATTR_WANTED(&va, va_fileid);
+ VATTR_WANTED(&va, va_modify_time);
+
+ if ((error = vnode_getattr(app_vp, &va, &context))) {
+ 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 = (u_long)va.va_fileid;
+ *mod = va.va_modify_time.tv_sec;
+
+ if (bsd_search_page_cache_data_base(
+ names_vp,
+ (struct profile_names_header *)names_buf,
+ app_name,
+ (unsigned int) va.va_modify_time.tv_sec,
+ (u_long)va.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 = 0;
+ *bufsize = 0;
+ 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) {
+ bsd_close_page_cache_files(uid_files);
+ thread_funnel_set(kernel_flock, funnel_state);
+ return ENOMEM;
+ }
+ *bufsize = profile_size;
+ while(profile_size) {
+ error = vn_rdwr(UIO_READ, data_vp,
+ (caddr_t) *buffer, profile_size,
+ profile, UIO_SYSSPACE32, IO_NODELOCKED,
+ kauth_cred_get(), &resid, p);
+ if((error) || (profile_size == resid)) {
+ 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;
+ }
+ bsd_close_page_cache_files(uid_files);
+ thread_funnel_set(kernel_flock, funnel_state);
+ return 0;
+ } else {
+ 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;
+ unsigned int resid;
+ vm_offset_t local_buf = 0;
+
+ 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<ele_total; i++) {
+ if((mod_date == element[i].mod_date)
+ && (inode == element[i].inode)) {
+ if(strncmp(element[i].name, app_name, 12) == 0) {
+ *profile = element[i].addr;
+ *profile_size = element[i].size;
+ if(local_buf != 0) {
+ kmem_free(kernel_map, local_buf, 4 * PAGE_SIZE);
+ }
+ return 0;
+ }
+ }
+ }
+ if(extended_list == 0)
+ break;
+ if(local_buf == 0) {
+ ret = kmem_alloc(kernel_map, &local_buf, 4 * PAGE_SIZE);
+ if(ret != KERN_SUCCESS) {
+ return ENOMEM;
+ }
+ }
+ element = (struct profile_element *)local_buf;
+ ele_total = extended_list;
+ extended_list = 0;
+ file_off += 4 * PAGE_SIZE;
+ if((ele_total * sizeof(struct profile_element)) >
+ (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,
+ CAST_DOWN(caddr_t, (local_buf + resid_off)),
+ size, file_off + resid_off, UIO_SYSSPACE32,
+ IO_NODELOCKED, kauth_cred_get(), &resid, p);
+ if((error) || (size == resid)) {
+ if(local_buf != 0) {
+ kmem_free(kernel_map, local_buf, 4 * PAGE_SIZE);
+ }
+ return EINVAL;
+ }
+ resid_off += size-resid;
+ size = resid;
+ }
+ }
+ if(local_buf != 0) {
+ kmem_free(kernel_map, 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;
+ int resid;
+ off_t resid_off;
+ int error;
+ boolean_t funnel_state;
+ off_t file_size;
+ struct vfs_context context;
+ off_t profile;
+ unsigned int profile_size;
+
+ vm_offset_t names_buf;
+ struct vnode *names_vp;
+ struct vnode *data_vp;
+ 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;
+
+ /* Stat data file for size */
+
+ context.vc_proc = p;
+ context.vc_ucred = kauth_cred_get();
+
+ if ((error = vnode_size(data_vp, &file_size, &context)) != 0) {
+ 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 = file_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 = file_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_SYSSPACE32, IO_NODELOCKED,
+ kauth_cred_get(), &resid, p);
+ if(error) {
+ printf("bsd_write_page_cache_file: Can't write name_element %x\n", user);
+ 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_SYSSPACE32, IO_NODELOCKED,
+ kauth_cred_get(), &resid, p);
+ if(error) {
+ 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 = file_size;
+ while(size) {
+ error = vn_rdwr(UIO_WRITE, data_vp,
+ (caddr_t)buffer, size, resid_off,
+ UIO_SYSSPACE32, IO_NODELOCKED,
+ kauth_cred_get(), &resid, p);
+ if(error) {
+ 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;
+ }
+ bsd_close_page_cache_files(uid_files);
+ thread_funnel_set(kernel_flock, funnel_state);
+ return 0;
+ }
+ /* Someone else wrote a twin profile before us */
+ bsd_close_page_cache_files(uid_files);
+ thread_funnel_set(kernel_flock, funnel_state);
+ return 0;
+ } else {
+ bsd_close_page_cache_files(uid_files);
+ thread_funnel_set(kernel_flock, funnel_state);
+ return EINVAL;
+ }
+
+}
+
+int
+prepare_profile_database(int user)
+{
+ const char *cache_path = "/var/vm/app_profile/";
+ struct proc *p;
+ int error;
+ int resid;
+ off_t resid_off;
+ 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 vnode_attr va;
+ struct vfs_context context;
+
+ struct profile_names_header *profile_header;
+ kern_return_t ret;
+
+ struct nameidata nd_names;
+ struct nameidata nd_data;
+
+ p = current_proc();
+
+ context.vc_proc = p;
+ context.vc_ucred = kauth_cred_get();
+
+ 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_SYSSPACE32, CAST_USER_ADDR_T(profile_names_string), &context);
+ NDINIT(&nd_data, LOOKUP, FOLLOW,
+ UIO_SYSSPACE32, CAST_USER_ADDR_T(profile_data_string), &context);
+
+ 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;
+
+ 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);
+
+ vnode_rele(data_vp);
+ vnode_put(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_SYSSPACE32, IO_NODELOCKED,
+ kauth_cred_get(), &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);
+
+ vnode_rele(names_vp);
+ vnode_put(names_vp);
+ vnode_rele(data_vp);
+ vnode_put(data_vp);
+
+ return error;
+ }
+ buf_ptr += size-resid;
+ resid_off += size-resid;
+ size = resid;
+ }
+ VATTR_INIT(&va);
+ VATTR_SET(&va, va_uid, user);
+
+ error = vnode_setattr(names_vp, &va, &context);
+ if(error) {
+ printf("prepare_profile_database: "
+ "Can't set user %s\n", profile_names_string);
+ }
+ vnode_rele(names_vp);
+ vnode_put(names_vp);
+
+ VATTR_INIT(&va);
+ VATTR_SET(&va, va_uid, user);
+ error = vnode_setattr(data_vp, &va, &context);
+ if(error) {
+ printf("prepare_profile_database: "
+ "Can't set user %s\n", profile_data_string);
+ }
+ vnode_rele(data_vp);
+ vnode_put(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;
+
+}