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 */ 66 typedef 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 75 static 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 */ 85 static int 86 add_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 */ 133 static mftsd_t * 134 get_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*/ 179 static int 180 process(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 */ 239 int 240 find_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 280 out: 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 */ 303 void 304 free_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