manifest_find.c revision 11996:91b62f7b8186
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*
28 * The primary role of this file is to obtain a list of manifests that are
29 * located in a specified directory or one of its subdirectories.  The
30 * find_manifests() function provides this service, and
31 * free_manifest_array() is used to free the memory associated with the
32 * returned list.
33 *
34 * The find_manifests() function can return an array consisting of all the
35 * .xml files in the directory and its subdirectories.  Alternatively,
36 * find_manifests() can be asked to only return new manifests based on the
37 * return of mhash_test_file().  The list that is returned is an array of
38 * pointers to manifest_info structures.
39 *
40 * Implementation Notes:
41 * ====================
42 * This module makes use of the nftw(3C) function to scan the directory.
43 * nftw() calls a processing function for every file that it finds.
44 * Unfortunately, nftw does not allow us to pass in any structure pointers
45 * to the processing function, and that makes it hard to accumulate a list.
46 * Thus, we will use the thread specific data area to hold data that must
47 * be retained between calls to the processing function.  This will allow
48 * this module to be used in multi-threaded applications if the need
49 * arises.
50 */
51
52#include <assert.h>
53#include <errno.h>
54#include <ftw.h>
55#include <libscf.h>
56#include <libuutil.h>
57#include <pthread.h>
58#include <stdlib.h>
59#include <string.h>
60#include "manifest_find.h"
61#include "manifest_hash.h"
62
63#define	MAX_DEPTH	24
64
65/* Thread specific data */
66typedef struct mftsd {
67	manifest_info_t ** tsd_array;	/* Array of manifest_info structs */
68	int		tsd_count;	/* Number items in list */
69	int		tsd_max;	/* Number of pointers allocated */
70					/* at tsd_array. */
71	int		tsd_flags;	/* Check flags for hash and extension */
72	scf_handle_t	*tsd_hndl;	/* Handle for libscf. */
73} mftsd_t;
74
75static pthread_key_t tsd_key = PTHREAD_ONCE_KEY_NP;
76
77/*
78 * Add the manifest info consisting of filename (fn), hash property name
79 * (pname) and hash to the array at tsd_array.  If necessary, realloc()
80 * will be called to increase the size of the buffer at tsd_array.
81 *
82 * Returns 0 on success and -1 on failure.  If a failure occurs, errno will
83 * be set.
84 */
85static int
86add_pointer(mftsd_t *tsdp, const char *fn, const char *pname, uchar_t *hash)
87{
88	manifest_info_t *info;
89	manifest_info_t **newblock;
90	int new_max;
91
92	if (tsdp->tsd_count >= (tsdp->tsd_max - 1)) {
93		/* Need more memory. */
94		new_max = (tsdp->tsd_max == 0) ? 16 : 2 * tsdp->tsd_max;
95		newblock = realloc(tsdp->tsd_array,
96		    new_max * sizeof (*tsdp->tsd_array));
97		if (newblock == NULL)
98			return (-1);
99		tsdp->tsd_array = newblock;
100		/* NULL terminate list in case allocations fail below. */
101		*(tsdp->tsd_array + tsdp->tsd_count) = NULL;
102		tsdp->tsd_max = new_max;
103	}
104	info = uu_zalloc(sizeof (*info));
105	if (info == NULL) {
106		errno = ENOMEM;
107		return (-1);
108	}
109	info->mi_path = uu_strdup(fn);
110	if (info->mi_path == NULL) {
111		uu_free(info);
112		errno = ENOMEM;
113		return (-1);
114	}
115	info->mi_prop = pname;
116	if (hash != NULL)
117		(void) memcpy(info->mi_hash, hash, MHASH_SIZE);
118	*(tsdp->tsd_array + tsdp->tsd_count) = info;
119	tsdp->tsd_count++;
120
121	/* NULL terminate the list. */
122	*(tsdp->tsd_array + tsdp->tsd_count) = NULL;
123
124	return (0);
125}
126
127/*
128 * If necessary initialize the thread specific data key at tsd_key, and
129 * allocate a mftsd_t structure to hold our thread specific data.  Upon
130 * success, the address the thread specific data is returned.  On failure,
131 * NULL is returned and errno is set.
132 */
133static mftsd_t *
134get_thread_specific_data()
135{
136	mftsd_t *tsdp;
137
138	if (pthread_key_create_once_np(&tsd_key, NULL) != 0)
139		return (NULL);
140	tsdp = (mftsd_t *)pthread_getspecific(tsd_key);
141	if (tsdp == NULL) {
142		/*
143		 * First time for this thread.  We need to allocate memory
144		 * for our thread specific data.
145		 */
146		tsdp = uu_zalloc(sizeof (*tsdp));
147		if (tsdp == NULL) {
148			errno = ENOMEM;
149			return (NULL);
150		}
151		errno = pthread_setspecific(tsd_key, tsdp);
152		if (errno != 0) {
153			/*
154			 * EINVAL means that our key is invalid, which
155			 * would be a coding error.
156			 */
157			assert(errno != EINVAL);
158			return (NULL);
159		}
160	}
161	return (tsdp);
162}
163
164/*
165 * This function is called by nftw(3C) every time that it finds an object
166 * in a directory of interest.  If the object is a file, process() checks
167 * to see if it is a manifest file by insuring that it has a .xml
168 * extension.
169 *
170 * If the file is a manifest file, and the CHECKHASH flag is set process()
171 * calls mhash_test_file() to see if it is a new manifest.  Manfest data
172 * for selected manifests is added to tsd_array in our thread specific data.
173 *
174 * The CHECKEXT flag may be set if this was not a directory search request
175 * but a single manifest file check that was determined by the caller to
176 * be found based not on the extension of the file.
177 */
178/*ARGSUSED*/
179static int
180process(const char *fn, const struct stat *sp, int ftw_type,
181    struct FTW *ftws)
182{
183	char *suffix_match;
184	uchar_t hash[MHASH_SIZE];
185	char *pname;
186	mftsd_t *tsdp;
187
188	if (ftw_type != FTW_F)
189		return (0);
190
191	tsdp = get_thread_specific_data();
192	if (tsdp == NULL)
193		return (-1);
194
195	/*
196	 * Only check the extension on the file when
197	 * requested.
198	 */
199	if (tsdp->tsd_flags & CHECKEXT) {
200		suffix_match = strstr(fn, ".xml");
201		if (suffix_match == NULL || strcmp(suffix_match, ".xml") != 0)
202			return (0);
203	}
204
205	if (tsdp->tsd_flags & CHECKHASH) {
206		if (mhash_test_file(tsdp->tsd_hndl, fn, 0, &pname, hash) ==
207		    MHASH_NEWFILE) {
208			return (add_pointer(tsdp, fn, pname, hash));
209		}
210	} else {
211		return (add_pointer(tsdp, fn, NULL, NULL));
212	}
213
214	return (0);
215}
216
217/*
218 * This function returns a pointer to an array of manifest_info_t pointers.
219 * There is one manifest_info_t pointer for each manifest file in the
220 * directory, dir, that satifies the selection criteria.  The array is
221 * returned to arrayp.  The array will be terminated with a NULL pointer.
222 * It is the responsibility of the caller to free the memory associated
223 * with the array by calling free_manifest_array().
224 *
225 * flags :
226 * 	0x1 - CHECKHASH - do the hash check and only return manifest
227 * 	files that do not have a hash entry in the smf/manifest table
228 * 	or the hash value has changed due to the manifest file having
229 * 	been modified.  If not set then all manifest files found are
230 * 	returned, regardless of the hash status.
231 *
232 * 	0x2 - CHECKEXT - Check the extension of the file is .xml
233 *
234 * On success a count of the number of selected manifests is returned.
235 * Note, however, that *arrayp will be set to NULL if the selection is
236 * empty, and a count of 0 will be returned.  In the case of failure, -1
237 * will be returned and errno will be set.
238 */
239int
240find_manifests(const char *dir, manifest_info_t ***arrayp, int flags)
241{
242	mftsd_t *tsdp;
243	int status = -1;
244	int count;
245
246	tsdp = get_thread_specific_data();
247	if (tsdp == NULL)
248		return (NULL);
249
250	tsdp->tsd_flags = flags;
251
252	/*
253	 * Create a handle for use by mhast_test_file() if
254	 * the flag is set to request hash checking be enabled.
255	 */
256	if (tsdp->tsd_flags & CHECKHASH) {
257		tsdp->tsd_hndl = scf_handle_create(SCF_VERSION);
258		if (tsdp->tsd_hndl == NULL) {
259			if (scf_error() == SCF_ERROR_NO_MEMORY) {
260				errno = ENOMEM;
261			} else {
262				errno = EINVAL;
263			}
264			goto out;
265		}
266		if (scf_handle_bind(tsdp->tsd_hndl) != SCF_SUCCESS) {
267			if (scf_error() == SCF_ERROR_NO_RESOURCES) {
268				errno = ENOMEM;
269			} else {
270				errno = EINVAL;
271			}
272			goto out;
273		}
274	}
275
276	if (nftw(dir, process, MAX_DEPTH, FTW_MOUNT) == 0) {
277		status = 0;
278	}
279
280out:
281	if (tsdp->tsd_hndl != NULL) {
282		(void) scf_handle_unbind(tsdp->tsd_hndl);
283		(void) scf_handle_destroy(tsdp->tsd_hndl);
284	}
285	if (status == 0) {
286		*arrayp = tsdp->tsd_array;
287		count = tsdp->tsd_count;
288	} else {
289		*arrayp = NULL;
290		free_manifest_array(tsdp->tsd_array);
291		count = -1;
292	}
293
294	/* Reset thread specific data. */
295	(void) memset(tsdp, 0, sizeof (*tsdp));
296
297	return (count);
298}
299
300/*
301 * Free the memory associated with the array of manifest_info structures.
302 */
303void
304free_manifest_array(manifest_info_t **array)
305{
306	manifest_info_t **entry;
307	manifest_info_t *info;
308
309	if (array == NULL)
310		return;
311
312	for (entry = array; *entry != NULL; entry++) {
313		info = *entry;
314		uu_free((void *) info->mi_path);
315		uu_free((void *) info->mi_prop);
316		uu_free(info);
317	}
318
319	/*
320	 * Array is allocated with realloc(3C), so it must be freed with
321	 * free(3c) rather than uu_free().
322	 */
323	free(array);
324}
325