xref: /onnv-gate/usr/src/cmd/svc/common/manifest_find.c (revision 13112:e0ee41527b6e)
111996SThomas.Whitten@Sun.COM /*
211996SThomas.Whitten@Sun.COM  * CDDL HEADER START
311996SThomas.Whitten@Sun.COM  *
411996SThomas.Whitten@Sun.COM  * The contents of this file are subject to the terms of the
511996SThomas.Whitten@Sun.COM  * Common Development and Distribution License (the "License").
611996SThomas.Whitten@Sun.COM  * You may not use this file except in compliance with the License.
711996SThomas.Whitten@Sun.COM  *
811996SThomas.Whitten@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
911996SThomas.Whitten@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1011996SThomas.Whitten@Sun.COM  * See the License for the specific language governing permissions
1111996SThomas.Whitten@Sun.COM  * and limitations under the License.
1211996SThomas.Whitten@Sun.COM  *
1311996SThomas.Whitten@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1411996SThomas.Whitten@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1511996SThomas.Whitten@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1611996SThomas.Whitten@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1711996SThomas.Whitten@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1811996SThomas.Whitten@Sun.COM  *
1911996SThomas.Whitten@Sun.COM  * CDDL HEADER END
2011996SThomas.Whitten@Sun.COM  */
2111996SThomas.Whitten@Sun.COM 
2211996SThomas.Whitten@Sun.COM /*
2312418STruong.Q.Nguyen@Sun.COM  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
2411996SThomas.Whitten@Sun.COM  */
2511996SThomas.Whitten@Sun.COM 
2611996SThomas.Whitten@Sun.COM /*
2711996SThomas.Whitten@Sun.COM  * The primary role of this file is to obtain a list of manifests that are
2811996SThomas.Whitten@Sun.COM  * located in a specified directory or one of its subdirectories.  The
2911996SThomas.Whitten@Sun.COM  * find_manifests() function provides this service, and
3011996SThomas.Whitten@Sun.COM  * free_manifest_array() is used to free the memory associated with the
3111996SThomas.Whitten@Sun.COM  * returned list.
3211996SThomas.Whitten@Sun.COM  *
3311996SThomas.Whitten@Sun.COM  * The find_manifests() function can return an array consisting of all the
3411996SThomas.Whitten@Sun.COM  * .xml files in the directory and its subdirectories.  Alternatively,
3511996SThomas.Whitten@Sun.COM  * find_manifests() can be asked to only return new manifests based on the
3611996SThomas.Whitten@Sun.COM  * return of mhash_test_file().  The list that is returned is an array of
3711996SThomas.Whitten@Sun.COM  * pointers to manifest_info structures.
3811996SThomas.Whitten@Sun.COM  *
3911996SThomas.Whitten@Sun.COM  * Implementation Notes:
4011996SThomas.Whitten@Sun.COM  * ====================
4111996SThomas.Whitten@Sun.COM  * This module makes use of the nftw(3C) function to scan the directory.
4211996SThomas.Whitten@Sun.COM  * nftw() calls a processing function for every file that it finds.
4311996SThomas.Whitten@Sun.COM  * Unfortunately, nftw does not allow us to pass in any structure pointers
4411996SThomas.Whitten@Sun.COM  * to the processing function, and that makes it hard to accumulate a list.
4511996SThomas.Whitten@Sun.COM  * Thus, we will use the thread specific data area to hold data that must
4611996SThomas.Whitten@Sun.COM  * be retained between calls to the processing function.  This will allow
4711996SThomas.Whitten@Sun.COM  * this module to be used in multi-threaded applications if the need
4811996SThomas.Whitten@Sun.COM  * arises.
4911996SThomas.Whitten@Sun.COM  */
5011996SThomas.Whitten@Sun.COM 
5111996SThomas.Whitten@Sun.COM #include <assert.h>
5211996SThomas.Whitten@Sun.COM #include <errno.h>
5311996SThomas.Whitten@Sun.COM #include <ftw.h>
5411996SThomas.Whitten@Sun.COM #include <libscf.h>
5511996SThomas.Whitten@Sun.COM #include <libuutil.h>
5611996SThomas.Whitten@Sun.COM #include <pthread.h>
5711996SThomas.Whitten@Sun.COM #include <stdlib.h>
5811996SThomas.Whitten@Sun.COM #include <string.h>
5911996SThomas.Whitten@Sun.COM #include "manifest_find.h"
6011996SThomas.Whitten@Sun.COM #include "manifest_hash.h"
6111996SThomas.Whitten@Sun.COM 
6211996SThomas.Whitten@Sun.COM #define	MAX_DEPTH	24
6311996SThomas.Whitten@Sun.COM 
6411996SThomas.Whitten@Sun.COM /* Thread specific data */
6511996SThomas.Whitten@Sun.COM typedef struct mftsd {
6611996SThomas.Whitten@Sun.COM 	manifest_info_t ** tsd_array;	/* Array of manifest_info structs */
6711996SThomas.Whitten@Sun.COM 	int		tsd_count;	/* Number items in list */
6811996SThomas.Whitten@Sun.COM 	int		tsd_max;	/* Number of pointers allocated */
6911996SThomas.Whitten@Sun.COM 					/* at tsd_array. */
7011996SThomas.Whitten@Sun.COM 	int		tsd_flags;	/* Check flags for hash and extension */
7111996SThomas.Whitten@Sun.COM 	scf_handle_t	*tsd_hndl;	/* Handle for libscf. */
7211996SThomas.Whitten@Sun.COM } mftsd_t;
7311996SThomas.Whitten@Sun.COM 
7411996SThomas.Whitten@Sun.COM static pthread_key_t tsd_key = PTHREAD_ONCE_KEY_NP;
7511996SThomas.Whitten@Sun.COM 
7611996SThomas.Whitten@Sun.COM /*
7711996SThomas.Whitten@Sun.COM  * Add the manifest info consisting of filename (fn), hash property name
7811996SThomas.Whitten@Sun.COM  * (pname) and hash to the array at tsd_array.  If necessary, realloc()
7911996SThomas.Whitten@Sun.COM  * will be called to increase the size of the buffer at tsd_array.
8011996SThomas.Whitten@Sun.COM  *
8111996SThomas.Whitten@Sun.COM  * Returns 0 on success and -1 on failure.  If a failure occurs, errno will
8211996SThomas.Whitten@Sun.COM  * be set.
8311996SThomas.Whitten@Sun.COM  */
8411996SThomas.Whitten@Sun.COM static int
add_pointer(mftsd_t * tsdp,const char * fn,const char * pname,uchar_t * hash)8511996SThomas.Whitten@Sun.COM add_pointer(mftsd_t *tsdp, const char *fn, const char *pname, uchar_t *hash)
8611996SThomas.Whitten@Sun.COM {
8711996SThomas.Whitten@Sun.COM 	manifest_info_t *info;
8811996SThomas.Whitten@Sun.COM 	manifest_info_t **newblock;
8911996SThomas.Whitten@Sun.COM 	int new_max;
9011996SThomas.Whitten@Sun.COM 
9111996SThomas.Whitten@Sun.COM 	if (tsdp->tsd_count >= (tsdp->tsd_max - 1)) {
9211996SThomas.Whitten@Sun.COM 		/* Need more memory. */
9311996SThomas.Whitten@Sun.COM 		new_max = (tsdp->tsd_max == 0) ? 16 : 2 * tsdp->tsd_max;
9411996SThomas.Whitten@Sun.COM 		newblock = realloc(tsdp->tsd_array,
9511996SThomas.Whitten@Sun.COM 		    new_max * sizeof (*tsdp->tsd_array));
9611996SThomas.Whitten@Sun.COM 		if (newblock == NULL)
9711996SThomas.Whitten@Sun.COM 			return (-1);
9811996SThomas.Whitten@Sun.COM 		tsdp->tsd_array = newblock;
9911996SThomas.Whitten@Sun.COM 		/* NULL terminate list in case allocations fail below. */
10011996SThomas.Whitten@Sun.COM 		*(tsdp->tsd_array + tsdp->tsd_count) = NULL;
10111996SThomas.Whitten@Sun.COM 		tsdp->tsd_max = new_max;
10211996SThomas.Whitten@Sun.COM 	}
10311996SThomas.Whitten@Sun.COM 	info = uu_zalloc(sizeof (*info));
10411996SThomas.Whitten@Sun.COM 	if (info == NULL) {
10511996SThomas.Whitten@Sun.COM 		errno = ENOMEM;
10611996SThomas.Whitten@Sun.COM 		return (-1);
10711996SThomas.Whitten@Sun.COM 	}
10811996SThomas.Whitten@Sun.COM 	info->mi_path = uu_strdup(fn);
10911996SThomas.Whitten@Sun.COM 	if (info->mi_path == NULL) {
11011996SThomas.Whitten@Sun.COM 		uu_free(info);
11111996SThomas.Whitten@Sun.COM 		errno = ENOMEM;
11211996SThomas.Whitten@Sun.COM 		return (-1);
11311996SThomas.Whitten@Sun.COM 	}
11411996SThomas.Whitten@Sun.COM 	info->mi_prop = pname;
11511996SThomas.Whitten@Sun.COM 	if (hash != NULL)
11611996SThomas.Whitten@Sun.COM 		(void) memcpy(info->mi_hash, hash, MHASH_SIZE);
11711996SThomas.Whitten@Sun.COM 	*(tsdp->tsd_array + tsdp->tsd_count) = info;
11811996SThomas.Whitten@Sun.COM 	tsdp->tsd_count++;
11911996SThomas.Whitten@Sun.COM 
12011996SThomas.Whitten@Sun.COM 	/* NULL terminate the list. */
12111996SThomas.Whitten@Sun.COM 	*(tsdp->tsd_array + tsdp->tsd_count) = NULL;
12211996SThomas.Whitten@Sun.COM 
12311996SThomas.Whitten@Sun.COM 	return (0);
12411996SThomas.Whitten@Sun.COM }
12511996SThomas.Whitten@Sun.COM 
12611996SThomas.Whitten@Sun.COM /*
12711996SThomas.Whitten@Sun.COM  * If necessary initialize the thread specific data key at tsd_key, and
12811996SThomas.Whitten@Sun.COM  * allocate a mftsd_t structure to hold our thread specific data.  Upon
12911996SThomas.Whitten@Sun.COM  * success, the address the thread specific data is returned.  On failure,
13011996SThomas.Whitten@Sun.COM  * NULL is returned and errno is set.
13111996SThomas.Whitten@Sun.COM  */
13211996SThomas.Whitten@Sun.COM static mftsd_t *
get_thread_specific_data()13311996SThomas.Whitten@Sun.COM get_thread_specific_data()
13411996SThomas.Whitten@Sun.COM {
13511996SThomas.Whitten@Sun.COM 	mftsd_t *tsdp;
13611996SThomas.Whitten@Sun.COM 
13711996SThomas.Whitten@Sun.COM 	if (pthread_key_create_once_np(&tsd_key, NULL) != 0)
13811996SThomas.Whitten@Sun.COM 		return (NULL);
13911996SThomas.Whitten@Sun.COM 	tsdp = (mftsd_t *)pthread_getspecific(tsd_key);
14011996SThomas.Whitten@Sun.COM 	if (tsdp == NULL) {
14111996SThomas.Whitten@Sun.COM 		/*
14211996SThomas.Whitten@Sun.COM 		 * First time for this thread.  We need to allocate memory
14311996SThomas.Whitten@Sun.COM 		 * for our thread specific data.
14411996SThomas.Whitten@Sun.COM 		 */
14511996SThomas.Whitten@Sun.COM 		tsdp = uu_zalloc(sizeof (*tsdp));
14611996SThomas.Whitten@Sun.COM 		if (tsdp == NULL) {
14711996SThomas.Whitten@Sun.COM 			errno = ENOMEM;
14811996SThomas.Whitten@Sun.COM 			return (NULL);
14911996SThomas.Whitten@Sun.COM 		}
15011996SThomas.Whitten@Sun.COM 		errno = pthread_setspecific(tsd_key, tsdp);
15111996SThomas.Whitten@Sun.COM 		if (errno != 0) {
15211996SThomas.Whitten@Sun.COM 			/*
15311996SThomas.Whitten@Sun.COM 			 * EINVAL means that our key is invalid, which
15411996SThomas.Whitten@Sun.COM 			 * would be a coding error.
15511996SThomas.Whitten@Sun.COM 			 */
15611996SThomas.Whitten@Sun.COM 			assert(errno != EINVAL);
15711996SThomas.Whitten@Sun.COM 			return (NULL);
15811996SThomas.Whitten@Sun.COM 		}
15911996SThomas.Whitten@Sun.COM 	}
16011996SThomas.Whitten@Sun.COM 	return (tsdp);
16111996SThomas.Whitten@Sun.COM }
16211996SThomas.Whitten@Sun.COM 
16311996SThomas.Whitten@Sun.COM /*
16411996SThomas.Whitten@Sun.COM  * This function is called by nftw(3C) every time that it finds an object
16511996SThomas.Whitten@Sun.COM  * in a directory of interest.  If the object is a file, process() checks
16612418STruong.Q.Nguyen@Sun.COM  * to see if it is a service bundle file by insuring that it has a .xml
16711996SThomas.Whitten@Sun.COM  * extension.
16811996SThomas.Whitten@Sun.COM  *
16912418STruong.Q.Nguyen@Sun.COM  * If the file is a service bundle file, and the CHECKHASH flag is set process()
17012418STruong.Q.Nguyen@Sun.COM  * calls mhash_test_file() to see if it is a new bundle.  Bundle file data
17112418STruong.Q.Nguyen@Sun.COM  * for selected bundles is added to tsd_array in our thread specific data.
17212418STruong.Q.Nguyen@Sun.COM  *
17312418STruong.Q.Nguyen@Sun.COM  * Assume given file is a manifest unless BUNDLE_PROF flag is set to indicate
17412418STruong.Q.Nguyen@Sun.COM  * it's a profile. For profile bundles, call mhash_test_file() with the
17512418STruong.Q.Nguyen@Sun.COM  * appropriate argument.
17611996SThomas.Whitten@Sun.COM  *
17711996SThomas.Whitten@Sun.COM  * The CHECKEXT flag may be set if this was not a directory search request
17812418STruong.Q.Nguyen@Sun.COM  * but a single service bundle file check that was determined by the caller to
17911996SThomas.Whitten@Sun.COM  * be found based not on the extension of the file.
18011996SThomas.Whitten@Sun.COM  */
18111996SThomas.Whitten@Sun.COM /*ARGSUSED*/
18211996SThomas.Whitten@Sun.COM static int
process(const char * fn,const struct stat * sp,int ftw_type,struct FTW * ftws)18311996SThomas.Whitten@Sun.COM process(const char *fn, const struct stat *sp, int ftw_type,
18411996SThomas.Whitten@Sun.COM     struct FTW *ftws)
18511996SThomas.Whitten@Sun.COM {
18612418STruong.Q.Nguyen@Sun.COM 	int is_profile;
18711996SThomas.Whitten@Sun.COM 	char *suffix_match;
18811996SThomas.Whitten@Sun.COM 	uchar_t hash[MHASH_SIZE];
18911996SThomas.Whitten@Sun.COM 	char *pname;
19011996SThomas.Whitten@Sun.COM 	mftsd_t *tsdp;
19111996SThomas.Whitten@Sun.COM 
19211996SThomas.Whitten@Sun.COM 	if (ftw_type != FTW_F)
19311996SThomas.Whitten@Sun.COM 		return (0);
19411996SThomas.Whitten@Sun.COM 
19511996SThomas.Whitten@Sun.COM 	tsdp = get_thread_specific_data();
19611996SThomas.Whitten@Sun.COM 	if (tsdp == NULL)
19711996SThomas.Whitten@Sun.COM 		return (-1);
19811996SThomas.Whitten@Sun.COM 
19911996SThomas.Whitten@Sun.COM 	/*
20011996SThomas.Whitten@Sun.COM 	 * Only check the extension on the file when
20111996SThomas.Whitten@Sun.COM 	 * requested.
20211996SThomas.Whitten@Sun.COM 	 */
20311996SThomas.Whitten@Sun.COM 	if (tsdp->tsd_flags & CHECKEXT) {
20411996SThomas.Whitten@Sun.COM 		suffix_match = strstr(fn, ".xml");
20511996SThomas.Whitten@Sun.COM 		if (suffix_match == NULL || strcmp(suffix_match, ".xml") != 0)
20611996SThomas.Whitten@Sun.COM 			return (0);
20711996SThomas.Whitten@Sun.COM 	}
20811996SThomas.Whitten@Sun.COM 
20911996SThomas.Whitten@Sun.COM 	if (tsdp->tsd_flags & CHECKHASH) {
21012418STruong.Q.Nguyen@Sun.COM 		is_profile = (tsdp->tsd_flags & BUNDLE_PROF) ? 1 : 0;
21112418STruong.Q.Nguyen@Sun.COM 		if (mhash_test_file(tsdp->tsd_hndl, fn, is_profile, &pname,
21212418STruong.Q.Nguyen@Sun.COM 		    hash) == MHASH_NEWFILE) {
21311996SThomas.Whitten@Sun.COM 			return (add_pointer(tsdp, fn, pname, hash));
21411996SThomas.Whitten@Sun.COM 		}
21511996SThomas.Whitten@Sun.COM 	} else {
21611996SThomas.Whitten@Sun.COM 		return (add_pointer(tsdp, fn, NULL, NULL));
21711996SThomas.Whitten@Sun.COM 	}
21811996SThomas.Whitten@Sun.COM 
21911996SThomas.Whitten@Sun.COM 	return (0);
22011996SThomas.Whitten@Sun.COM }
22111996SThomas.Whitten@Sun.COM 
22211996SThomas.Whitten@Sun.COM /*
22311996SThomas.Whitten@Sun.COM  * This function returns a pointer to an array of manifest_info_t pointers.
22412418STruong.Q.Nguyen@Sun.COM  * There is one manifest_info_t pointer for each service bundle file in the
22511996SThomas.Whitten@Sun.COM  * directory, dir, that satifies the selection criteria.  The array is
22611996SThomas.Whitten@Sun.COM  * returned to arrayp.  The array will be terminated with a NULL pointer.
22711996SThomas.Whitten@Sun.COM  * It is the responsibility of the caller to free the memory associated
22811996SThomas.Whitten@Sun.COM  * with the array by calling free_manifest_array().
22911996SThomas.Whitten@Sun.COM  *
23011996SThomas.Whitten@Sun.COM  * flags :
23112418STruong.Q.Nguyen@Sun.COM  * 	0x1 - CHECKHASH - do the hash check and only return bundle
23211996SThomas.Whitten@Sun.COM  * 	files that do not have a hash entry in the smf/manifest table
23312418STruong.Q.Nguyen@Sun.COM  * 	or the hash value has changed due to the bundle file having
23412418STruong.Q.Nguyen@Sun.COM  * 	been modified.  If not set then all service bundle files found
23512418STruong.Q.Nguyen@Sun.COM  * 	are returned, regardless of the hash status.
23611996SThomas.Whitten@Sun.COM  *
23711996SThomas.Whitten@Sun.COM  * 	0x2 - CHECKEXT - Check the extension of the file is .xml
23811996SThomas.Whitten@Sun.COM  *
23912418STruong.Q.Nguyen@Sun.COM  * On success a count of the number of selected bundles is returned.
24011996SThomas.Whitten@Sun.COM  * Note, however, that *arrayp will be set to NULL if the selection is
24111996SThomas.Whitten@Sun.COM  * empty, and a count of 0 will be returned.  In the case of failure, -1
24211996SThomas.Whitten@Sun.COM  * will be returned and errno will be set.
243*13112STony.Q.Nguyen@oracle.com  *
244*13112STony.Q.Nguyen@oracle.com  * This function takes a repository handle argument from the caller and saves
245*13112STony.Q.Nguyen@oracle.com  * that handle in a thread specific data structure. The thread specific
246*13112STony.Q.Nguyen@oracle.com  * repository handle is used in process() to communicate with the appropriate
247*13112STony.Q.Nguyen@oracle.com  * repository. Thus callers should take care of thread safety with respect to
248*13112STony.Q.Nguyen@oracle.com  * the repository handle. Currently, the two callers of find_manifests are both
249*13112STony.Q.Nguyen@oracle.com  * single threaded, i.e. svccfg and mfstscan, so thread safety not an issue.
25011996SThomas.Whitten@Sun.COM  */
25111996SThomas.Whitten@Sun.COM int
find_manifests(scf_handle_t * hndl,const char * dir,manifest_info_t *** arrayp,int flags)252*13112STony.Q.Nguyen@oracle.com find_manifests(scf_handle_t *hndl, const char *dir,
253*13112STony.Q.Nguyen@oracle.com     manifest_info_t ***arrayp, int flags)
25411996SThomas.Whitten@Sun.COM {
25511996SThomas.Whitten@Sun.COM 	mftsd_t *tsdp;
25611996SThomas.Whitten@Sun.COM 	int status = -1;
25711996SThomas.Whitten@Sun.COM 	int count;
25811996SThomas.Whitten@Sun.COM 
25911996SThomas.Whitten@Sun.COM 	tsdp = get_thread_specific_data();
26011996SThomas.Whitten@Sun.COM 	if (tsdp == NULL)
26111996SThomas.Whitten@Sun.COM 		return (NULL);
26211996SThomas.Whitten@Sun.COM 
26311996SThomas.Whitten@Sun.COM 	tsdp->tsd_flags = flags;
26411996SThomas.Whitten@Sun.COM 
26511996SThomas.Whitten@Sun.COM 	if (tsdp->tsd_flags & CHECKHASH) {
266*13112STony.Q.Nguyen@oracle.com 		tsdp->tsd_hndl = hndl;
26711996SThomas.Whitten@Sun.COM 	}
26811996SThomas.Whitten@Sun.COM 
26911996SThomas.Whitten@Sun.COM 	if (nftw(dir, process, MAX_DEPTH, FTW_MOUNT) == 0) {
27011996SThomas.Whitten@Sun.COM 		status = 0;
27111996SThomas.Whitten@Sun.COM 	}
27211996SThomas.Whitten@Sun.COM 
27311996SThomas.Whitten@Sun.COM out:
27411996SThomas.Whitten@Sun.COM 	if (status == 0) {
27511996SThomas.Whitten@Sun.COM 		*arrayp = tsdp->tsd_array;
27611996SThomas.Whitten@Sun.COM 		count = tsdp->tsd_count;
27711996SThomas.Whitten@Sun.COM 	} else {
27811996SThomas.Whitten@Sun.COM 		*arrayp = NULL;
27911996SThomas.Whitten@Sun.COM 		free_manifest_array(tsdp->tsd_array);
28011996SThomas.Whitten@Sun.COM 		count = -1;
28111996SThomas.Whitten@Sun.COM 	}
28211996SThomas.Whitten@Sun.COM 
28311996SThomas.Whitten@Sun.COM 	/* Reset thread specific data. */
28411996SThomas.Whitten@Sun.COM 	(void) memset(tsdp, 0, sizeof (*tsdp));
28511996SThomas.Whitten@Sun.COM 
28611996SThomas.Whitten@Sun.COM 	return (count);
28711996SThomas.Whitten@Sun.COM }
28811996SThomas.Whitten@Sun.COM 
28911996SThomas.Whitten@Sun.COM /*
29011996SThomas.Whitten@Sun.COM  * Free the memory associated with the array of manifest_info structures.
29111996SThomas.Whitten@Sun.COM  */
29211996SThomas.Whitten@Sun.COM void
free_manifest_array(manifest_info_t ** array)29311996SThomas.Whitten@Sun.COM free_manifest_array(manifest_info_t **array)
29411996SThomas.Whitten@Sun.COM {
29511996SThomas.Whitten@Sun.COM 	manifest_info_t **entry;
29611996SThomas.Whitten@Sun.COM 	manifest_info_t *info;
29711996SThomas.Whitten@Sun.COM 
29811996SThomas.Whitten@Sun.COM 	if (array == NULL)
29911996SThomas.Whitten@Sun.COM 		return;
30011996SThomas.Whitten@Sun.COM 
30111996SThomas.Whitten@Sun.COM 	for (entry = array; *entry != NULL; entry++) {
30211996SThomas.Whitten@Sun.COM 		info = *entry;
30311996SThomas.Whitten@Sun.COM 		uu_free((void *) info->mi_path);
30411996SThomas.Whitten@Sun.COM 		uu_free((void *) info->mi_prop);
30511996SThomas.Whitten@Sun.COM 		uu_free(info);
30611996SThomas.Whitten@Sun.COM 	}
30711996SThomas.Whitten@Sun.COM 
30811996SThomas.Whitten@Sun.COM 	/*
30911996SThomas.Whitten@Sun.COM 	 * Array is allocated with realloc(3C), so it must be freed with
31011996SThomas.Whitten@Sun.COM 	 * free(3c) rather than uu_free().
31111996SThomas.Whitten@Sun.COM 	 */
31211996SThomas.Whitten@Sun.COM 	free(array);
31311996SThomas.Whitten@Sun.COM }
314