Back to index

openldap  2.4.31
attr.c
Go to the documentation of this file.
00001 /* attr.c - routines for dealing with attributes */
00002 /* $OpenLDAP$ */
00003 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00004  *
00005  * Copyright 1998-2012 The OpenLDAP Foundation.
00006  * All rights reserved.
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted only as authorized by the OpenLDAP
00010  * Public License.
00011  *
00012  * A copy of this license is available in the file LICENSE in the
00013  * top-level directory of the distribution or, alternatively, at
00014  * <http://www.OpenLDAP.org/license.html>.
00015  */
00016 /* Portions Copyright (c) 1995 Regents of the University of Michigan.
00017  * All rights reserved.
00018  *
00019  * Redistribution and use in source and binary forms are permitted
00020  * provided that this notice is preserved and that due credit is given
00021  * to the University of Michigan at Ann Arbor. The name of the University
00022  * may not be used to endorse or promote products derived from this
00023  * software without specific prior written permission. This software
00024  * is provided ``as is'' without express or implied warranty.
00025  */
00026 
00027 #include "portable.h"
00028 
00029 #include <stdio.h>
00030 
00031 #ifdef HAVE_FCNTL_H
00032 #include <fcntl.h>
00033 #endif
00034 
00035 #include <ac/ctype.h>
00036 #include <ac/errno.h>
00037 #include <ac/socket.h>
00038 #include <ac/string.h>
00039 #include <ac/time.h>
00040 
00041 #include "slap.h"
00042 
00043 /*
00044  * Allocate in chunks, minimum of 1000 at a time.
00045  */
00046 #define       CHUNK_SIZE    1000
00047 typedef struct slap_list {
00048        struct slap_list *next;
00049 } slap_list;
00050 static slap_list *attr_chunks;
00051 static Attribute *attr_list;
00052 static ldap_pvt_thread_mutex_t attr_mutex;
00053 
00054 int
00055 attr_prealloc( int num )
00056 {
00057        Attribute *a;
00058        slap_list *s;
00059 
00060        if (!num) return 0;
00061 
00062        s = ch_calloc( 1, sizeof(slap_list) + num * sizeof(Attribute));
00063        s->next = attr_chunks;
00064        attr_chunks = s;
00065 
00066        a = (Attribute *)(s+1);
00067        for ( ;num>1; num--) {
00068               a->a_next = a+1;
00069               a++;
00070        }
00071        a->a_next = attr_list;
00072        attr_list = (Attribute *)(s+1);
00073 
00074        return 0;
00075 }
00076 
00077 Attribute *
00078 attr_alloc( AttributeDescription *ad )
00079 {
00080        Attribute *a;
00081 
00082        ldap_pvt_thread_mutex_lock( &attr_mutex );
00083        if ( !attr_list )
00084               attr_prealloc( CHUNK_SIZE );
00085        a = attr_list;
00086        attr_list = a->a_next;
00087        a->a_next = NULL;
00088        ldap_pvt_thread_mutex_unlock( &attr_mutex );
00089        
00090        a->a_desc = ad;
00091        if ( ad && ( ad->ad_type->sat_flags & SLAP_AT_SORTED_VAL ))
00092               a->a_flags |= SLAP_ATTR_SORTED_VALS;
00093 
00094        return a;
00095 }
00096 
00097 /* Return a list of num attrs */
00098 Attribute *
00099 attrs_alloc( int num )
00100 {
00101        Attribute *head = NULL;
00102        Attribute **a;
00103 
00104        ldap_pvt_thread_mutex_lock( &attr_mutex );
00105        for ( a = &attr_list; *a && num > 0; a = &(*a)->a_next ) {
00106               if ( !head )
00107                      head = *a;
00108               num--;
00109        }
00110        attr_list = *a;
00111        if ( num > 0 ) {
00112               attr_prealloc( num > CHUNK_SIZE ? num : CHUNK_SIZE );
00113               *a = attr_list;
00114               for ( ; *a && num > 0; a = &(*a)->a_next ) {
00115                      if ( !head )
00116                             head = *a;
00117                      num--;
00118               }
00119               attr_list = *a;
00120        }
00121        *a = NULL;
00122        ldap_pvt_thread_mutex_unlock( &attr_mutex );
00123 
00124        return head;
00125 }
00126 
00127 
00128 void
00129 attr_clean( Attribute *a )
00130 {
00131        if ( a->a_nvals && a->a_nvals != a->a_vals &&
00132               !( a->a_flags & SLAP_ATTR_DONT_FREE_VALS )) {
00133               if ( a->a_flags & SLAP_ATTR_DONT_FREE_DATA ) {
00134                      free( a->a_nvals );
00135               } else {
00136                      ber_bvarray_free( a->a_nvals );
00137               }
00138        }
00139        /* a_vals may be equal to slap_dummy_bv, a static empty berval;
00140         * this is used as a placeholder for attributes that do not carry
00141         * values, e.g. when proxying search entries with the "attrsonly"
00142         * bit set. */
00143        if ( a->a_vals != &slap_dummy_bv &&
00144               !( a->a_flags & SLAP_ATTR_DONT_FREE_VALS )) {
00145               if ( a->a_flags & SLAP_ATTR_DONT_FREE_DATA ) {
00146                      free( a->a_vals );
00147               } else {
00148                      ber_bvarray_free( a->a_vals );
00149               }
00150        }
00151        a->a_desc = NULL;
00152        a->a_vals = NULL;
00153        a->a_nvals = NULL;
00154 #ifdef LDAP_COMP_MATCH
00155        a->a_comp_data = NULL;
00156 #endif
00157        a->a_flags = 0;
00158        a->a_numvals = 0;
00159 }
00160 
00161 void
00162 attr_free( Attribute *a )
00163 {
00164        attr_clean( a );
00165        ldap_pvt_thread_mutex_lock( &attr_mutex );
00166        a->a_next = attr_list;
00167        attr_list = a;
00168        ldap_pvt_thread_mutex_unlock( &attr_mutex );
00169 }
00170 
00171 #ifdef LDAP_COMP_MATCH
00172 void
00173 comp_tree_free( Attribute *a )
00174 {
00175        Attribute *next;
00176 
00177        for( ; a != NULL ; a = next ) {
00178               next = a->a_next;
00179               if ( component_destructor && a->a_comp_data ) {
00180                      if ( a->a_comp_data->cd_mem_op )
00181                             component_destructor( a->a_comp_data->cd_mem_op );
00182                      free ( a->a_comp_data );
00183               }
00184        }
00185 }
00186 #endif
00187 
00188 void
00189 attrs_free( Attribute *a )
00190 {
00191        if ( a ) {
00192               Attribute *b = (Attribute *)0xBAD, *tail, *next;
00193 
00194               /* save tail */
00195               tail = a;
00196               do {
00197                      next = a->a_next;
00198                      attr_clean( a );
00199                      a->a_next = b;
00200                      b = a;
00201                      a = next;
00202               } while ( next );
00203 
00204               ldap_pvt_thread_mutex_lock( &attr_mutex );
00205               /* replace NULL with current attr list and let attr list
00206                * start from last attribute returned to list */
00207               tail->a_next = attr_list;
00208               attr_list = b;
00209               ldap_pvt_thread_mutex_unlock( &attr_mutex );
00210        }
00211 }
00212 
00213 static void
00214 attr_dup2( Attribute *tmp, Attribute *a )
00215 {
00216        tmp->a_flags = a->a_flags & SLAP_ATTR_PERSISTENT_FLAGS;
00217        if ( a->a_vals != NULL ) {
00218               unsigned      i, j;
00219 
00220               tmp->a_numvals = a->a_numvals;
00221               tmp->a_vals = ch_malloc( (tmp->a_numvals + 1) * sizeof(struct berval) );
00222               for ( i = 0; i < tmp->a_numvals; i++ ) {
00223                      ber_dupbv( &tmp->a_vals[i], &a->a_vals[i] );
00224                      if ( BER_BVISNULL( &tmp->a_vals[i] ) ) break;
00225                      /* FIXME: error? */
00226               }
00227               BER_BVZERO( &tmp->a_vals[i] );
00228 
00229               /* a_nvals must be non null; it may be equal to a_vals */
00230               assert( a->a_nvals != NULL );
00231 
00232               if ( a->a_nvals != a->a_vals ) {
00233 
00234                      tmp->a_nvals = ch_malloc( (tmp->a_numvals + 1) * sizeof(struct berval) );
00235                      j = 0;
00236                      if ( i ) {
00237                             for ( ; !BER_BVISNULL( &a->a_nvals[j] ); j++ ) {
00238                                    assert( j < i );
00239                                    ber_dupbv( &tmp->a_nvals[j], &a->a_nvals[j] );
00240                                    if ( BER_BVISNULL( &tmp->a_nvals[j] ) ) break;
00241                                    /* FIXME: error? */
00242                             }
00243                             assert( j == i );
00244                      }
00245                      BER_BVZERO( &tmp->a_nvals[j] );
00246 
00247               } else {
00248                      tmp->a_nvals = tmp->a_vals;
00249               }
00250        }
00251 }
00252 
00253 Attribute *
00254 attr_dup( Attribute *a )
00255 {
00256        Attribute *tmp;
00257 
00258        if ( a == NULL) return NULL;
00259 
00260        tmp = attr_alloc( a->a_desc );
00261        attr_dup2( tmp, a );
00262        return tmp;
00263 }
00264 
00265 Attribute *
00266 attrs_dup( Attribute *a )
00267 {
00268        int i;
00269        Attribute *tmp, *anew;
00270 
00271        if( a == NULL ) return NULL;
00272 
00273        /* count them */
00274        for( tmp=a,i=0; tmp; tmp=tmp->a_next ) {
00275               i++;
00276        }
00277 
00278        anew = attrs_alloc( i );
00279 
00280        for( tmp=anew; a; a=a->a_next ) {
00281               tmp->a_desc = a->a_desc;
00282               attr_dup2( tmp, a );
00283               tmp=tmp->a_next;
00284        }
00285 
00286        return anew;
00287 }
00288 
00289 int
00290 attr_valfind(
00291        Attribute *a,
00292        unsigned flags,
00293        struct berval *val,
00294        unsigned *slot,
00295        void *ctx )
00296 {
00297        struct berval nval = BER_BVNULL, *cval;
00298        MatchingRule *mr;
00299        const char *text;
00300        int match = -1, rc;
00301        unsigned i, n;
00302 
00303        if ( flags & SLAP_MR_ORDERING )
00304               mr = a->a_desc->ad_type->sat_ordering;
00305        else
00306               mr = a->a_desc->ad_type->sat_equality;
00307 
00308        if( !SLAP_IS_MR_ASSERTED_VALUE_NORMALIZED_MATCH( flags ) &&
00309               mr->smr_normalize )
00310        {
00311               rc = (mr->smr_normalize)(
00312                      flags & (SLAP_MR_TYPE_MASK|SLAP_MR_SUBTYPE_MASK|SLAP_MR_VALUE_OF_SYNTAX),
00313                      a->a_desc->ad_type->sat_syntax,
00314                      mr, val, &nval, ctx );
00315 
00316               if( rc != LDAP_SUCCESS ) {
00317                      return LDAP_INVALID_SYNTAX;
00318               }
00319               cval = &nval;
00320        } else {
00321               cval = val;
00322        }
00323 
00324        n = a->a_numvals;
00325        if ( (a->a_flags & SLAP_ATTR_SORTED_VALS) && n ) {
00326               /* Binary search */
00327               unsigned base = 0;
00328 
00329               do {
00330                      unsigned pivot = n >> 1;
00331                      i = base + pivot;
00332                      rc = value_match( &match, a->a_desc, mr, flags,
00333                             &a->a_nvals[i], cval, &text );
00334                      if ( rc == LDAP_SUCCESS && match == 0 )
00335                             break;
00336                      if ( match < 0 ) {
00337                             base = i+1;
00338                             n -= pivot+1;
00339                      } else {
00340                             n = pivot;
00341                      }
00342               } while ( n );
00343               if ( match < 0 )
00344                      i++;
00345        } else {
00346        /* Linear search */
00347               for ( i = 0; i < n; i++ ) {
00348                      const char *text;
00349 
00350                      rc = ordered_value_match( &match, a->a_desc, mr, flags,
00351                             &a->a_nvals[i], cval, &text );
00352                      if ( rc == LDAP_SUCCESS && match == 0 )
00353                             break;
00354               }
00355        }
00356        if ( match )
00357               rc = LDAP_NO_SUCH_ATTRIBUTE;
00358        if ( slot )
00359               *slot = i;
00360        if ( nval.bv_val )
00361               slap_sl_free( nval.bv_val, ctx );
00362 
00363        return rc;
00364 }
00365 
00366 int
00367 attr_valadd(
00368        Attribute *a,
00369        BerVarray vals,
00370        BerVarray nvals,
00371        int nn )
00372 {
00373        int           i;
00374        BerVarray     v2;
00375 
00376        v2 = (BerVarray) SLAP_REALLOC( (char *) a->a_vals,
00377                   (a->a_numvals + nn + 1) * sizeof(struct berval) );
00378        if( v2 == NULL ) {
00379               Debug(LDAP_DEBUG_TRACE,
00380                 "attr_valadd: SLAP_REALLOC failed.\n", 0, 0, 0 );
00381               return LBER_ERROR_MEMORY;
00382        }
00383        a->a_vals = v2;
00384        if ( nvals ) {
00385               v2 = (BerVarray) SLAP_REALLOC( (char *) a->a_nvals,
00386                             (a->a_numvals + nn + 1) * sizeof(struct berval) );
00387               if( v2 == NULL ) {
00388                      Debug(LDAP_DEBUG_TRACE,
00389                        "attr_valadd: SLAP_REALLOC failed.\n", 0, 0, 0 );
00390                      return LBER_ERROR_MEMORY;
00391               }
00392               a->a_nvals = v2;
00393        } else {
00394               a->a_nvals = a->a_vals;
00395        }
00396 
00397        /* If sorted and old vals exist, must insert */
00398        if (( a->a_flags & SLAP_ATTR_SORTED_VALS ) && a->a_numvals ) {
00399               unsigned slot;
00400               int j, rc;
00401               v2 = nvals ? nvals : vals;
00402               for ( i = 0; i < nn; i++ ) {
00403                      rc = attr_valfind( a, SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX |
00404                             SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH | SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH,
00405                             &v2[i], &slot, NULL );
00406                      if ( rc != LDAP_NO_SUCH_ATTRIBUTE ) {
00407                             /* should never happen */
00408                             if ( rc == LDAP_SUCCESS )
00409                                    rc = LDAP_TYPE_OR_VALUE_EXISTS;
00410                             return rc;
00411                      }
00412                      for ( j = a->a_numvals; j >= (int)slot; j-- ) {
00413                             a->a_vals[j+1] = a->a_vals[j];
00414                             if ( nvals )
00415                                    a->a_nvals[j+1] = a->a_nvals[j];
00416                      }
00417                      ber_dupbv( &a->a_nvals[slot], &v2[i] );
00418                      if ( nvals )
00419                             ber_dupbv( &a->a_vals[slot], &vals[i] );
00420                      a->a_numvals++;
00421               }
00422               BER_BVZERO( &a->a_vals[a->a_numvals] );
00423               if ( a->a_vals != a->a_nvals )
00424                      BER_BVZERO( &a->a_nvals[a->a_numvals] );
00425        } else {
00426               v2 = &a->a_vals[a->a_numvals];
00427               for ( i = 0 ; i < nn; i++ ) {
00428                      ber_dupbv( &v2[i], &vals[i] );
00429                      if ( BER_BVISNULL( &v2[i] ) ) break;
00430               }
00431               BER_BVZERO( &v2[i] );
00432 
00433               if ( nvals ) {
00434                      v2 = &a->a_nvals[a->a_numvals];
00435                      for ( i = 0 ; i < nn; i++ ) {
00436                             ber_dupbv( &v2[i], &nvals[i] );
00437                             if ( BER_BVISNULL( &v2[i] ) ) break;
00438                      }
00439                      BER_BVZERO( &v2[i] );
00440               }
00441               a->a_numvals += i;
00442        }
00443        return 0;
00444 }
00445 
00446 /*
00447  * attr_merge - merge the given type and value with the list of
00448  * attributes in attrs.
00449  *
00450  * nvals must be NULL if the attribute has no normalizer.
00451  * In this case, a->a_nvals will be set equal to a->a_vals.
00452  *
00453  * returns    0      everything went ok
00454  *            -1     trouble
00455  */
00456 
00457 int
00458 attr_merge(
00459        Entry         *e,
00460        AttributeDescription *desc,
00461        BerVarray     vals,
00462        BerVarray     nvals )
00463 {
00464        int i = 0;
00465 
00466        Attribute     **a;
00467 
00468        for ( a = &e->e_attrs; *a != NULL; a = &(*a)->a_next ) {
00469               if (  (*a)->a_desc == desc ) {
00470                      break;
00471               }
00472        }
00473 
00474        if ( *a == NULL ) {
00475               *a = attr_alloc( desc );
00476        } else {
00477               /*
00478                * FIXME: if the attribute already exists, the presence
00479                * of nvals and the value of (*a)->a_nvals must be consistent
00480                */
00481               assert( ( nvals == NULL && (*a)->a_nvals == (*a)->a_vals )
00482                             || ( nvals != NULL && (
00483                                    ( (*a)->a_vals == NULL && (*a)->a_nvals == NULL )
00484                                    || ( (*a)->a_nvals != (*a)->a_vals ) ) ) );
00485        }
00486 
00487        if ( vals != NULL ) {
00488               for ( ; !BER_BVISNULL( &vals[i] ); i++ ) ;
00489        }
00490        return attr_valadd( *a, vals, nvals, i );
00491 }
00492 
00493 /*
00494  * if a normalization function is defined for the equality matchingRule
00495  * of desc, the value is normalized and stored in nval; otherwise nval 
00496  * is NULL
00497  */
00498 int
00499 attr_normalize(
00500        AttributeDescription *desc,
00501        BerVarray            vals,
00502        BerVarray            *nvalsp,
00503        void                 *memctx )
00504 {
00505        int           rc = LDAP_SUCCESS;
00506        BerVarray     nvals = NULL;
00507 
00508        *nvalsp = NULL;
00509 
00510        if ( desc->ad_type->sat_equality &&
00511               desc->ad_type->sat_equality->smr_normalize )
00512        {
00513               int    i;
00514               
00515               for ( i = 0; !BER_BVISNULL( &vals[i] ); i++ );
00516 
00517               nvals = slap_sl_calloc( sizeof(struct berval), i + 1, memctx );
00518               for ( i = 0; !BER_BVISNULL( &vals[i] ); i++ ) {
00519                      rc = desc->ad_type->sat_equality->smr_normalize(
00520                                    SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
00521                                    desc->ad_type->sat_syntax,
00522                                    desc->ad_type->sat_equality,
00523                                    &vals[i], &nvals[i], memctx );
00524 
00525                      if ( rc != LDAP_SUCCESS ) {
00526                             BER_BVZERO( &nvals[i + 1] );
00527                             break;
00528                      }
00529               }
00530               BER_BVZERO( &nvals[i] );
00531               *nvalsp = nvals;
00532        }
00533 
00534        if ( rc != LDAP_SUCCESS && nvals != NULL ) {
00535               ber_bvarray_free_x( nvals, memctx );
00536        }
00537 
00538        return rc;
00539 }
00540 
00541 int
00542 attr_merge_normalize(
00543        Entry                *e,
00544        AttributeDescription *desc,
00545        BerVarray            vals,
00546        void                 *memctx )
00547 {
00548        BerVarray     nvals = NULL;
00549        int           rc;
00550 
00551        rc = attr_normalize( desc, vals, &nvals, memctx );
00552        if ( rc == LDAP_SUCCESS ) {
00553               rc = attr_merge( e, desc, vals, nvals );
00554               if ( nvals != NULL ) {
00555                      ber_bvarray_free_x( nvals, memctx );
00556               }
00557        }
00558 
00559        return rc;
00560 }
00561 
00562 int
00563 attr_merge_one(
00564        Entry         *e,
00565        AttributeDescription *desc,
00566        struct berval *val,
00567        struct berval *nval )
00568 {
00569        Attribute     **a;
00570 
00571        for ( a = &e->e_attrs; *a != NULL; a = &(*a)->a_next ) {
00572               if ( (*a)->a_desc == desc ) {
00573                      break;
00574               }
00575        }
00576 
00577        if ( *a == NULL ) {
00578               *a = attr_alloc( desc );
00579        }
00580 
00581        return attr_valadd( *a, val, nval, 1 );
00582 }
00583 
00584 /*
00585  * if a normalization function is defined for the equality matchingRule
00586  * of desc, the value is normalized and stored in nval; otherwise nval 
00587  * is NULL
00588  */
00589 int
00590 attr_normalize_one(
00591        AttributeDescription *desc,
00592        struct berval *val,
00593        struct berval *nval,
00594        void          *memctx )
00595 {
00596        int           rc = LDAP_SUCCESS;
00597 
00598        BER_BVZERO( nval );
00599 
00600        if ( desc->ad_type->sat_equality &&
00601               desc->ad_type->sat_equality->smr_normalize )
00602        {
00603               rc = desc->ad_type->sat_equality->smr_normalize(
00604                             SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
00605                             desc->ad_type->sat_syntax,
00606                             desc->ad_type->sat_equality,
00607                             val, nval, memctx );
00608 
00609               if ( rc != LDAP_SUCCESS ) {
00610                      return rc;
00611               }
00612        }
00613 
00614        return rc;
00615 }
00616 
00617 int
00618 attr_merge_normalize_one(
00619        Entry         *e,
00620        AttributeDescription *desc,
00621        struct berval *val,
00622        void          *memctx )
00623 {
00624        struct berval nval = BER_BVNULL;
00625        struct berval *nvalp = NULL;
00626        int           rc;
00627 
00628        rc = attr_normalize_one( desc, val, &nval, memctx );
00629        if ( rc == LDAP_SUCCESS && !BER_BVISNULL( &nval ) ) {
00630               nvalp = &nval;
00631        }
00632 
00633        rc = attr_merge_one( e, desc, val, nvalp );
00634        if ( nvalp != NULL ) {
00635               slap_sl_free( nval.bv_val, memctx );
00636        }
00637        return rc;
00638 }
00639 
00640 /*
00641  * attrs_find - find attribute(s) by AttributeDescription
00642  * returns next attribute which is subtype of provided description.
00643  */
00644 
00645 Attribute *
00646 attrs_find(
00647     Attribute *a,
00648        AttributeDescription *desc )
00649 {
00650        for ( ; a != NULL; a = a->a_next ) {
00651               if ( is_ad_subtype( a->a_desc, desc ) ) {
00652                      return( a );
00653               }
00654        }
00655 
00656        return( NULL );
00657 }
00658 
00659 /*
00660  * attr_find - find attribute by type
00661  */
00662 
00663 Attribute *
00664 attr_find(
00665     Attribute *a,
00666        AttributeDescription *desc )
00667 {
00668        for ( ; a != NULL; a = a->a_next ) {
00669               if ( a->a_desc == desc ) {
00670                      return( a );
00671               }
00672        }
00673 
00674        return( NULL );
00675 }
00676 
00677 /*
00678  * attr_delete - delete the attribute type in list pointed to by attrs
00679  * return     0      deleted ok
00680  *            1      not found in list a
00681  *            -1     something bad happened
00682  */
00683 
00684 int
00685 attr_delete(
00686     Attribute **attrs,
00687        AttributeDescription *desc )
00688 {
00689        Attribute     **a;
00690 
00691        for ( a = attrs; *a != NULL; a = &(*a)->a_next ) {
00692               if ( (*a)->a_desc == desc ) {
00693                      Attribute     *save = *a;
00694                      *a = (*a)->a_next;
00695                      attr_free( save );
00696 
00697                      return LDAP_SUCCESS;
00698               }
00699        }
00700 
00701        return LDAP_NO_SUCH_ATTRIBUTE;
00702 }
00703 
00704 int
00705 attr_init( void )
00706 {
00707        ldap_pvt_thread_mutex_init( &attr_mutex );
00708        return 0;
00709 }
00710 
00711 int
00712 attr_destroy( void )
00713 {
00714        slap_list *a;
00715 
00716        for ( a=attr_chunks; a; a=attr_chunks ) {
00717               attr_chunks = a->next;
00718               free( a );
00719        }
00720        ldap_pvt_thread_mutex_destroy( &attr_mutex );
00721        return 0;
00722 }