/* * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights * Reserved. This file contains Original Code and/or Modifications of * Original Code as defined in and that are subject to the Apple Public * Source License Version 1.0 (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. * * The 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@ */ /* * Server-side implementation of NetInfo interface * Copyright (C) 1989 by NeXT, Inc. */ #include #include #include #include #include "ni_server.h" #include "ni_file.h" #include "ni_globals.h" #include #include #include #include "index.h" #include "index_manager.h" #define NIOP_READ 0 #define NIOP_WRITE 1 /* * What's inside the opaque handle we pass to clients of this layer */ typedef struct ni_handle { ni_entrylist nilistres; /* result of ni_list (efficiency hack) */ char *user; /* username of caller */ void *file_hdl; /* hook into next layer */ im_handle im_hdl; /* handle into index manager */ } ni_handle; #define NH(handle) ((struct ni_handle *)handle) #define FH(handle) NH(handle)->file_hdl #define IMH(handle) (&NH(handle)->im_hdl) static ni_status ni_inaccesslist(void *handle, ni_namelist access_list) { if (ni_namelist_match(access_list, ACCESS_USER_ANYBODY) != NI_INDEX_NULL) { auth_count[WGOOD]++; if (!i_am_clone) { system_log(LOG_DEBUG, "Allowing any user [%s] to modify domain", (NH(handle)->user == NULL) ? "(unknown)" : NH(handle)->user); } return NI_OK; } if (NH(handle)->user == NULL) { auth_count[WBAD]++; system_log(LOG_ERR, "Anonymous user may not modify domain"); return NI_PERM; } if (ni_namelist_match(access_list, NH(handle)->user) != NI_INDEX_NULL) { auth_count[WGOOD]++; if (!i_am_clone) { system_log(LOG_DEBUG, "Allowing user %s to modify domain", NH(handle)->user); } return NI_OK; } auth_count[WBAD]++; system_log(LOG_ERR, "Remote user %s may not modify domain", NH(handle)->user); return NI_PERM; } static ni_status ni_validate_dir(void *handle, ni_object *obj, int complain) { ni_proplist *pl; ni_index i; if ((NH(handle)->user != NULL) && (ni_name_match(NH(handle)->user, ACCESS_USER_SUPER))) { auth_count[WGOOD]++; if (!i_am_clone) { system_log(LOG_DEBUG, "Allowing superuser %s to modify directory %d", NH(handle)->user, obj->nio_id.nii_object); } return NI_OK; } pl = &obj->nio_props; for (i = 0; i < pl->nipl_len; i++) { if (ni_name_match(pl->nipl_val[i].nip_name, ACCESS_DIR_KEY)) { return ni_inaccesslist(handle, pl->nipl_val[i].nip_val); } } if (complain == 0) return NI_PERM; auth_count[WBAD]++; system_log(LOG_ERR, "Remote user %s may not modify directory %d", (NH(handle)->user == NULL) ? "(unknown)" : NH(handle)->user, obj->nio_id.nii_object); return NI_PERM; } static ni_status ni_validate_name(void *handle, ni_object *obj, ni_index prop_index) { ni_proplist *pl; ni_name key = NULL; ni_name propkey; ni_index i; if ((NH(handle)->user != NULL) && (ni_name_match(NH(handle)->user, ACCESS_USER_SUPER))) { auth_count[WGOOD]++; if (!i_am_clone) { system_log(LOG_DEBUG, "Allowing superuser %s to modify " "property %s in directory %d", NH(handle)->user, obj->nio_props.nipl_val[prop_index].nip_name, obj->nio_id.nii_object); } return NI_OK; } propkey = obj->nio_props.nipl_val[prop_index].nip_name; MM_ALLOC_ARRAY(key, (strlen(ACCESS_NAME_PREFIX) + strlen(propkey) + 1)); sprintf(key, "%s%s", ACCESS_NAME_PREFIX, propkey); pl = &obj->nio_props; for (i = 0; i < pl->nipl_len; i++) { if (ni_name_match(pl->nipl_val[i].nip_name, key)) { ni_name_free(&key); return ni_inaccesslist(handle, pl->nipl_val[i].nip_val); } } auth_count[WBAD]++; system_log(LOG_ERR, "Remote user %s may not modify property %s in directory %d", NH(handle)->user, key, obj->nio_id.nii_object); ni_name_free(&key); return NI_PERM; } /* * Free an object */ static void obj_free(ni_object *obj) { int i, j, nvals, len; ni_property *p; if (obj == NULL) return; if (obj->nio_children.ni_idlist_len > 0) free(obj->nio_children.ni_idlist_val); len = obj->nio_props.ni_proplist_len; for (i = 0; i < len; i++) { p = &(obj->nio_props.ni_proplist_val[i]); free(p->nip_name); nvals = p->nip_val.ni_namelist_len; for (j = 0; j < nvals; j++) free(p->nip_val.ni_namelist_val[j]); free(p->nip_val.ni_namelist_val); } if (len > 0) free(obj->nio_props.ni_proplist_val); free(obj); } /* * Lookup an object. If we are about to do a write, * make sure object is not stale. */ static ni_status obj_lookup(void *handle, ni_id *idp, int op, ni_object **objp) { ni_status status; ni_id id; id = *idp; status = file_read(FH(handle), &id, objp); if (status != NI_OK) return status; if ((op == NIOP_WRITE) && (id.nii_instance != idp->nii_instance)) { obj_free(*objp); return NI_STALE; } *idp = id; return NI_OK; } /* * Allocates a new object, returned ID is arbitrary */ static ni_status obj_alloc(void *handle, ni_object **objp) { ni_object *obj; ni_status status; MM_ALLOC(obj); MM_ZERO(obj); status = file_idalloc(FH(handle), &obj->nio_id); if (status != NI_OK) { MM_FREE(obj); return status; } *objp = obj; return NI_OK; } /* * Destroys an object */ static void obj_unalloc(void *handle, ni_object *obj) { file_idunalloc(FH(handle), obj->nio_id); obj_free(obj); } /* * Initilialize this layer */ ni_status ni_init(char *rootdir, void **handle) { ni_handle *ni; ni_status status; MM_ALLOC(ni); ni->nilistres.ni_entrylist_len = 0; ni->nilistres.ni_entrylist_val = NULL; ni->user = NULL; status = file_init(rootdir, &ni->file_hdl); if (status != NI_OK) { system_log(LOG_ERR, "file_init failed: %s", ni_error(status)); MM_FREE(ni); return status; } ni->im_hdl = im_alloc(); /* * Make sure the index manager cache is flushed * when the store is changed by another process. */ dsstore_set_sync_delegate((dsstore *)ni->file_hdl, (void (*)(void *))im_forget, &ni->im_hdl); *handle = ni; return status; } /* * Return the database tag */ char * ni_tagname(void *handle) { ni_name tag; ni_name dot; tag = ni_name_dup(file_dirname(FH(handle))); dot = rindex(tag, '.'); if (dot != NULL) *dot = 0; return tag; } /* * Forget what we know (cache flushing) */ void ni_forget(void *handle) { file_forget(FH(handle)); im_forget(IMH(handle)); } /* * Rename this database */ void ni_renamedir(void *handle, char *name) { file_renamedir(FH(handle), name); } /* * Set the username of the caller */ ni_status ni_setuser(void *handle, ni_name_const user) { if (NH(handle)->user != NULL) ni_name_free(&NH(handle)->user); if (user != NULL) NH(handle)->user = ni_name_dup(user); else NH(handle)->user = NULL; return NI_OK; } /* * Free up allocated resources */ void ni_free(void *handle) { ni_list_const_free(handle); if (NH(handle)->user != NULL) ni_name_free(&NH(handle)->user); file_free(FH(handle)); im_free(IMH(handle)); MM_FREE(NH(handle)); } unsigned ni_getchecksum(void *handle) { return file_getchecksum(FH(handle)); } void ni_shutdown(void *handle, unsigned checksum) { file_shutdown(FH(handle), checksum); } ni_status ni_root(void *handle, ni_id *id_p) { ni_object *obj; ni_id root_id; ni_status status; root_id.nii_object = 0; root_id.nii_instance = 0; status = file_read(FH(handle), &root_id, &obj); if (status != NI_OK) { /* * Create root node */ MM_ALLOC(obj); MM_ZERO(obj); obj->nio_id.nii_object=-1; status = file_write(FH(handle), obj); if (status != NI_OK) { obj_free(obj); return status; } } root_id = obj->nio_id; obj_free(obj); *id_p = root_id; return NI_OK; } int ni_idlist_hasid(ni_idlist *idlist, ni_index id) { ni_index i; for (i = 0; i < idlist->ni_idlist_len; i++) { if (idlist->ni_idlist_val[i] == id) return 1; } return 0; } ni_status ni_create(void *handle, ni_id *parent_id, ni_proplist pl, ni_id *child_id_p, ni_index where) { ni_object *child; ni_object *parent; ni_status status; status = obj_lookup(handle, parent_id, NIOP_WRITE, &parent); if (status != NI_OK) return status; status = ni_validate_dir(handle, parent, 1); if (status != NI_OK) { obj_free(parent); return status; } status = obj_alloc(handle, &child); if (status != NI_OK) { obj_free(parent); return status; } child->nio_props = ni_proplist_dup(pl); child->nio_parent = parent_id->nii_object; status = file_write(FH(handle), child); if (status != NI_OK) { obj_free(parent); obj_unalloc(handle, child); return status; } *child_id_p = child->nio_id; /* * Update parent */ if (!ni_idlist_hasid(&parent->nio_children, child_id_p->nii_object)) { /* * Only insert if it isn't already there. This is so people * can fix databases with hard-linked directories. Hard links * shouldn't happen, but did in previous releases. The way * to fix it is to cause both links to be in the same * directory where they then merge. */ ni_idlist_insert(&parent->nio_children, child_id_p->nii_object, where); } status = file_write(FH(handle), parent); if (status != NI_OK) { obj_unalloc(handle, child); obj_free(parent); return status; } im_newnode(IMH(handle), child, where); *parent_id = parent->nio_id; obj_free(child); obj_free(parent); file_notify(FH(handle)); return NI_OK; } ni_status ni_destroy(void *handle, ni_id *parent_id, ni_id child_id) { ni_object *parent; ni_object *child; ni_status status; int badid = 0; status = obj_lookup(handle, parent_id, NIOP_WRITE, &parent); if (status != NI_OK) return status; status = ni_validate_dir(handle, parent, 1); if (status != NI_OK) { obj_free(parent); return status; } status = obj_lookup(handle, &child_id, NIOP_WRITE, &child); if (status != NI_OK) { if (status != NI_BADID) { obj_free(parent); return status; } else badid++; } if (!badid) { if (child->nio_children.ni_idlist_len != 0) { obj_free(child); obj_free(parent); return NI_NOTEMPTY; } } if (!ni_idlist_delete(&parent->nio_children, child_id.nii_object)) { obj_free(parent); if (!badid) obj_free(child); return NI_UNRELATED; } /* * Commit changes */ status = file_write(FH(handle), parent); if (status != NI_OK) { obj_free(parent); if (!badid) obj_free(child); return status; } *parent_id = parent->nio_id; obj_free(parent); if (!badid) { im_remove(IMH(handle), child); obj_unalloc(handle, child); } file_notify(FH(handle)); return NI_OK; } /* * Copy properties for read functions ONLY (ni_read, ni_lookupread). */ static inline void copyprops(ni_proplist *inprops, ni_proplist *outprops) { /* * Do not copy the data, only the pointers to it. * Zeroing out the props prevents obj_free() from freeing these * pointers. */ *outprops = *inprops; MM_ZERO(inprops); } ni_status ni_read(void *handle, ni_id *id, ni_proplist *props) { ni_status status; ni_object *obj; status = obj_lookup(handle, id, NIOP_READ, &obj); if (status != NI_OK) return status; copyprops(&obj->nio_props, props); obj_free(obj); return status; } ni_status ni_write(void *handle, ni_id *id, ni_proplist props) { ni_status status; ni_object *obj; ni_proplist saveprops; status = obj_lookup(handle, id, NIOP_WRITE, &obj); if (status != NI_OK) return status; status = ni_validate_dir(handle, obj, 1); if (status != NI_OK) { obj_free(obj); return status; } saveprops = obj->nio_props; obj->nio_props = ni_proplist_dup(props); status = file_write(FH(handle), obj); if (status != NI_OK) { ni_proplist_free(&saveprops); obj_free(obj); return status; } im_destroy_all(IMH(handle), obj, saveprops); ni_proplist_free(&saveprops); im_create_all(IMH(handle), obj, obj->nio_props); *id = obj->nio_id; obj_free(obj); file_notify(FH(handle)); return NI_OK; } static ni_index ni_prop_find(ni_proplist props, ni_name_const pname) { ni_index i; for (i = 0; i < props.nipl_len; i++) { if (ni_name_match(props.nipl_val[i].nip_name, pname)) return i; } return NI_INDEX_NULL; } ni_status ni_lookup(void *handle, ni_id *id, ni_name_const pname, ni_name_const pval, ni_idlist *children_p) { ni_object *obj, *tmp; ni_index i; ni_status status; ni_id tmpid; ni_idlist res; ni_index ndirs, *dirs, which, oid; index_handle index; int please_index; ni_property *prop; status = obj_lookup(handle, id, NIOP_READ, &obj); if (status != NI_OK) return status; if (im_has_indexed_dir(IMH(handle), id->nii_object, pname, pval, &dirs, &ndirs)) { res.ni_idlist_len = ndirs; res.ni_idlist_val = NULL; if (ndirs > 0) { MM_ALLOC_ARRAY(res.ni_idlist_val, res.ni_idlist_len); MM_BCOPY(dirs, res.ni_idlist_val, res.ni_idlist_len * sizeof(dirs[0])); } } else { res.ni_idlist_len = 0; res.ni_idlist_val = NULL; index = index_alloc(); please_index = 1; for (i = 0; i < obj->nio_children.ni_idlist_len; i++) { tmpid.nii_object = obj->nio_children.ni_idlist_val[i]; status = obj_lookup(handle, &tmpid, NIOP_READ, &tmp); if (status != NI_OK) { system_log(LOG_ERR, "cannot lookup child"); if (please_index) { index_unalloc(&index); please_index = 0; } continue; } which = ni_prop_find(tmp->nio_props, pname); if (which != NI_INDEX_NULL) { prop = &tmp->nio_props.nipl_val[which]; oid = tmp->nio_id.nii_object; if (ni_namelist_match(prop->nip_val, pval) != NI_INDEX_NULL) { ni_idlist_insert(&res, oid, NI_INDEX_NULL); } if (please_index) { index_insert_list(&index, prop->nip_val, oid); } } obj_free(tmp); } if (please_index) { im_store_index(IMH(handle), index, obj->nio_id.nii_object, pname); } } obj_free(obj); if (res.ni_idlist_len == 0) return NI_NODIR; *children_p = res; return NI_OK; } ni_status ni_lookupread(void *handle, ni_id *id, ni_name_const pname, ni_name_const pval, ni_proplist *props) { ni_object *obj; ni_object *tmp; ni_index i; ni_status status; ni_id tmpid; int found; ni_index ndirs; ni_index *dirs; ni_index which; index_handle index; int please_index; ni_index oid; ni_property *prop; status = obj_lookup(handle, id, NIOP_READ, &obj); if (status != NI_OK) return status; if (im_has_indexed_dir(IMH(handle), id->nii_object, pname, pval, &dirs, &ndirs)) { found = ndirs; if (found) { tmpid.nii_object = dirs[0]; status = obj_lookup(handle, &tmpid, NIOP_READ, &tmp); if (status != NI_OK) return status; copyprops(&tmp->nio_props, props); obj_free(tmp); } } else { index = index_alloc(); please_index = 1; found = 0; for (i = 0; (i < obj->nio_children.ni_idlist_len && (please_index || !found)); i++) { tmpid.nii_object = obj->nio_children.ni_idlist_val[i]; status = obj_lookup(handle, &tmpid, NIOP_READ, &tmp); if (status != NI_OK) { system_log(LOG_ERR, "cannot lookup child"); if (please_index) { index_unalloc(&index); please_index = 0; } continue; } which = ni_prop_find(tmp->nio_props, pname); if (which != NI_INDEX_NULL) { prop = &tmp->nio_props.nipl_val[which]; oid = tmp->nio_id.nii_object; if (ni_namelist_match(prop->nip_val, pval) != NI_INDEX_NULL) { if (!found) { copyprops(&tmp->nio_props, props); found++; } } if (please_index) { index_insert_list(&index, prop->nip_val, oid); } } obj_free(tmp); } if (please_index) { im_store_index(IMH(handle), index, obj->nio_id.nii_object, pname); } } obj_free(obj); return (found ? NI_OK : NI_NODIR); } void ni_list_const_free(void *handle) { if (NH(handle)->nilistres.ni_entrylist_val != NULL) { ni_entrylist_free(&NH(handle)->nilistres); NH(handle)->nilistres.ni_entrylist_len = 0; NH(handle)->nilistres.ni_entrylist_val = NULL; } } /* * Like ni_list, but for efficiency, the returned entries are to be * considered "const" and NOT freed. */ ni_status ni_list_const(void *handle, ni_id *id, ni_name_const pname, ni_entrylist *entries) { ni_object *obj; ni_object *tmp; ni_index i; ni_index j; ni_status status; ni_id tmpid; ni_proplist *pl; ni_namelist *nl; ni_entrylist res; int use_store; ni_list_const_free(handle); status = obj_lookup(handle, id, NIOP_READ, &obj); if (status != NI_OK) return status; use_store = (NIOP_READ == NIOP_READ); if (!use_store || !im_has_saved_list(IMH(handle), id->nii_object, pname, &res)) { res.ni_entrylist_len = obj->nio_children.ni_idlist_len; MM_ALLOC_ARRAY(res.ni_entrylist_val, res.ni_entrylist_len); MM_ZERO_ARRAY(res.ni_entrylist_val, res.ni_entrylist_len); for (i = 0; i < obj->nio_children.ni_idlist_len; i++) { tmpid.nii_object = obj->nio_children.ni_idlist_val[i]; status = obj_lookup(handle, &tmpid, NIOP_READ, &tmp); if (status != NI_OK) { system_log(LOG_ERR, "cannot lookup child"); continue; } res.ni_entrylist_val[i].id = tmpid.nii_object; res.ni_entrylist_val[i].names = NULL; pl = &tmp->nio_props; for (j = 0; j < pl->nipl_len; j++) { nl = &pl->nipl_val[j].nip_val; if (ni_name_match(pl->nipl_val[j].nip_name, pname) && nl->ninl_len > 0) { MM_ALLOC(res.ni_entrylist_val[i].names); (*res.ni_entrylist_val[i].names = ni_namelist_dup(*nl)); break; } } obj_free(tmp); } if (use_store) { im_store_list(IMH(handle), obj->nio_id.nii_object, pname, res); } NH(handle)->nilistres = res; } obj_free(obj); *entries = res; return NI_OK; } ni_status ni_listall(void *handle, ni_id *id, ni_proplist_list *entries) { ni_object *obj; ni_object *tmp; ni_index i; ni_status status; ni_id tmpid; ni_proplist_list res; status = obj_lookup(handle, id, NIOP_READ, &obj); if (status != NI_OK) return status; res.nipll_len = obj->nio_children.ni_idlist_len; MM_ALLOC_ARRAY(res.nipll_val, res.nipll_len); MM_ZERO_ARRAY(res.nipll_val, res.nipll_len); for (i = 0; i < obj->nio_children.ni_idlist_len; i++) { tmpid.nii_object = obj->nio_children.ni_idlist_val[i]; status = obj_lookup(handle, &tmpid, NIOP_READ, &tmp); if (status != NI_OK) { system_log(LOG_ERR, "cannot lookup child"); continue; } res.nipll_val[i] = ni_proplist_dup(tmp->nio_props); obj_free(tmp); } obj_free(obj); *entries = res; return NI_OK; } ni_status ni_children(void *handle, ni_id *id, ni_idlist *children_p) { ni_object *obj; ni_status status; status = obj_lookup(handle, id, NIOP_READ, &obj); if (status != NI_OK) return status; *children_p = ni_idlist_dup(obj->nio_children); obj_free(obj); return NI_OK; } ni_status ni_parent(void *handle, ni_id *id, ni_index *parent_id_p) { ni_object *obj; ni_status status; status = obj_lookup(handle, id, NIOP_READ, &obj); if (status != NI_OK) return status; *parent_id_p = obj->nio_parent; obj_free(obj); return NI_OK; } ni_status ni_self(void *handle, ni_id *id) { ni_object *obj; ni_status status; status = obj_lookup(handle, id, NIOP_READ, &obj); if (status != NI_OK) return status; id->nii_instance = obj->nio_id.nii_instance; id->nii_object = obj->nio_id.nii_object; obj_free(obj); return NI_OK; } ni_status ni_readprop(void *handle, ni_id *id, ni_index prop_index, ni_namelist *propval_p) { ni_status status; ni_object *obj; status = obj_lookup(handle, id, NIOP_READ, &obj); if (status != NI_OK) return status; if (prop_index >= obj->nio_props.nipl_len) { obj_free(obj); return NI_NOPROP; } *propval_p = ni_namelist_dup(obj->nio_props.nipl_val[prop_index].nip_val); obj_free(obj); return status; } ni_status ni_writeprop(void *handle, ni_id *id, ni_index prop_index, ni_namelist values) { ni_status status; ni_object *obj; ni_namelist savelist; status = obj_lookup(handle, id, NIOP_WRITE, &obj); if (status != NI_OK) return status; if (prop_index >= obj->nio_props.nipl_len) { obj_free(obj); return NI_NOPROP; } /* check for directory access */ status = ni_validate_dir(handle, obj, 0); if (status != NI_OK) { /* no directory access - check for access to this property */ status = ni_validate_name(handle, obj, prop_index); } if (status != NI_OK) { obj_free(obj); return status; } savelist = obj->nio_props.nipl_val[prop_index].nip_val; obj->nio_props.nipl_val[prop_index].nip_val = ni_namelist_dup(values); status = file_write(FH(handle), obj); if (status != NI_OK) { obj_free(obj); ni_namelist_free(&savelist); return status; } im_destroy_list(IMH(handle), obj, obj->nio_props.nipl_val[prop_index].nip_name, savelist); ni_namelist_free(&savelist); im_create_list(IMH(handle), obj, obj->nio_props.nipl_val[prop_index].nip_name, obj->nio_props.nipl_val[prop_index].nip_val); *id = obj->nio_id; obj_free(obj); file_notify(FH(handle)); return status; } ni_status ni_createprop(void *handle, ni_id *id, ni_property prop, ni_index where) { ni_status status; ni_object *obj; status = obj_lookup(handle, id, NIOP_WRITE, &obj); if (status != NI_OK) return status; status = ni_validate_dir(handle, obj, 1); if (status != NI_OK) { obj_free(obj); return status; } /* * Add property */ ni_proplist_insert(&obj->nio_props, prop, where); status = file_write(FH(handle), obj); if (status != NI_OK) { obj_free(obj); return status; } *id = obj->nio_id; im_create_list(IMH(handle), obj, prop.nip_name, prop.nip_val); obj_free(obj); file_notify(FH(handle)); return NI_OK; } ni_status ni_destroyprop(void *handle, ni_id *id, ni_index prop_index) { ni_status status; ni_object *obj; ni_property saveprop; status = obj_lookup(handle, id, NIOP_WRITE, &obj); if (status != NI_OK) return status; if (prop_index >= obj->nio_props.nipl_len) { obj_free(obj); return NI_NOPROP; } status = ni_validate_dir(handle, obj, 1); if (status != NI_OK) { obj_free(obj); return status; } /* * Save property, zero out old pointers so ni_proplist_delete() * doesn't free them. */ saveprop = ni_prop_dup(obj->nio_props.nipl_val[prop_index]); MM_ZERO(&obj->nio_props.nipl_val[prop_index]); ni_proplist_delete(&obj->nio_props, prop_index); status = file_write(FH(handle), obj); if (status != NI_OK) { obj_free(obj); ni_prop_free(&saveprop); return status; } *id = obj->nio_id; im_destroy_list(IMH(handle), obj, saveprop.nip_name, saveprop.nip_val); ni_prop_free(&saveprop); obj_free(obj); file_notify(FH(handle)); return NI_OK; } ni_status ni_renameprop(void *handle, ni_id *id, ni_index prop_index, ni_name_const name) { ni_status status; ni_object *obj; ni_name savename; status = obj_lookup(handle, id, NIOP_WRITE, &obj); if (status != NI_OK) return status; if (prop_index >= obj->nio_props.nipl_len) { obj_free(obj); return NI_NOPROP; } status = ni_validate_dir(handle, obj, 1); if (status != NI_OK) { obj_free(obj); return status; } savename = obj->nio_props.nipl_val[prop_index].nip_name; obj->nio_props.nipl_val[prop_index].nip_name = ni_name_dup(name); status = file_write(FH(handle), obj); if (status != NI_OK) { ni_name_free(&savename); obj_free(obj); return status; } im_destroy_list(IMH(handle), obj, savename, obj->nio_props.nipl_val[prop_index].nip_val); ni_name_free(&savename); im_create_list(IMH(handle), obj, obj->nio_props.nipl_val[prop_index].nip_name, obj->nio_props.nipl_val[prop_index].nip_val); *id = obj->nio_id; obj_free(obj); file_notify(FH(handle)); return NI_OK; } ni_status ni_listprops(void *handle, ni_id *id, ni_namelist *propnames) { ni_status status; ni_object *obj; ni_index i; status = obj_lookup(handle, id, NIOP_READ, &obj); if (status != NI_OK) return status; propnames->ninl_len = 0; propnames->ninl_val = NULL; for (i = 0; i < obj->nio_props.nipl_len; i++) { ni_namelist_insert(propnames, obj->nio_props.nipl_val[i].nip_name, NI_INDEX_NULL); } *id = obj->nio_id; obj_free(obj); return NI_OK; } ni_status ni_createname(void *handle, ni_id *id, ni_index prop_index, ni_name_const name, ni_index where) { ni_status status; ni_object *obj; status = obj_lookup(handle, id, NIOP_WRITE, &obj); if (status != NI_OK) return status; if (prop_index >= obj->nio_props.nipl_len) { obj_free(obj); return NI_NOPROP; } /* check for directory access */ status = ni_validate_dir(handle, obj, 0); if (status != NI_OK) { /* no directory access - check for access to this property */ status = ni_validate_name(handle, obj, prop_index); } if (status != NI_OK) { obj_free(obj); return status; } ni_namelist_insert(&obj->nio_props.nipl_val[prop_index].nip_val, name, where); status = file_write(FH(handle), obj); if (status != NI_OK) { obj_free(obj); return status; } im_create(IMH(handle), obj, obj->nio_props.nipl_val[prop_index].nip_name, name, where); *id = obj->nio_id; obj_free(obj); file_notify(FH(handle)); return NI_OK; } ni_status ni_destroyname(void *handle, ni_id *id, ni_index prop_index, ni_index name_index) { ni_status status; ni_object *obj; ni_namelist *nl; ni_name savename; status = obj_lookup(handle, id, NIOP_WRITE, &obj); if (status != NI_OK) return status; if (prop_index >= obj->nio_props.nipl_len) { obj_free(obj); return NI_NOPROP; } nl = &obj->nio_props.nipl_val[prop_index].nip_val; if (name_index >= nl->ninl_len) { obj_free(obj); return NI_NONAME; } /* check for directory access */ status = ni_validate_dir(handle, obj, 0); if (status != NI_OK) { /* no directory access - check for access to this property */ status = ni_validate_name(handle, obj, prop_index); } if (status != NI_OK) { obj_free(obj); return status; } /* * Copy name and set to NULL so ni_namelist_free() doesn't free it. */ savename = nl->ninl_val[name_index]; nl->ninl_val[name_index] = NULL; ni_namelist_delete(nl, name_index); status = file_write(FH(handle), obj); if (status != NI_OK) { obj_free(obj); ni_name_free(&savename); return status; } *id = obj->nio_id; im_destroy(IMH(handle), obj, obj->nio_props.nipl_val[prop_index].nip_name, savename, name_index); ni_name_free(&savename); obj_free(obj); return NI_OK; } ni_index ni_highestid(void *handle) { return file_highestid(FH(handle)); } ni_status ni_writename(void *handle, ni_id *id, ni_index prop_index, ni_index name_index, ni_name_const name) { ni_status status; ni_object *obj; ni_namelist *nl; ni_name savename; status = obj_lookup(handle, id, NIOP_WRITE, &obj); if (status != NI_OK) return status; if (prop_index >= obj->nio_props.nipl_len) return NI_NOPROP; nl = &obj->nio_props.nipl_val[prop_index].nip_val; if (name_index >= nl->ninl_len) { obj_free(obj); return NI_NONAME; } /* check for directory access */ status = ni_validate_dir(handle, obj, 0); if (status != NI_OK) { /* no directory access - check for access to this property */ status = ni_validate_name(handle, obj, prop_index); } if (status != NI_OK) { obj_free(obj); return status; } savename = nl->ninl_val[name_index]; nl->ninl_val[name_index] = ni_name_dup(name); status = file_write(FH(handle), obj); if (status != NI_OK) { obj_free(obj); ni_name_free(&savename); return status; } im_destroy(IMH(handle), obj, obj->nio_props.nipl_val[prop_index].nip_name, savename, name_index); ni_name_free(&savename); im_create(IMH(handle), obj, obj->nio_props.nipl_val[prop_index].nip_name, name, name_index); *id = obj->nio_id; obj_free(obj); file_notify(FH(handle)); return NI_OK; } ni_status ni_readname(void *handle, ni_id *id, ni_index prop_index, ni_index name_index, ni_name *name) { ni_status status; ni_object *obj; ni_namelist *nl; status = obj_lookup(handle, id, NIOP_READ, &obj); if (status != NI_OK) return status; if (prop_index >= obj->nio_props.nipl_len) { obj_free(obj); return NI_NOPROP; } nl = &obj->nio_props.nipl_val[prop_index].nip_val; if (name_index >= nl->ninl_len) { obj_free(obj); return NI_NONAME; } *name = ni_name_dup(nl->ninl_val[name_index]); obj_free(obj); return NI_OK; }