test-getgr.c revision 291759
1/*- 2 * Copyright (c) 2006 Michael Bushkov <bushman@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 */ 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD: stable/10/tools/regression/lib/libc/nss/test-getgr.c 291759 2015-12-04 09:18:12Z ngie $"); 30 31#include <arpa/inet.h> 32#include <assert.h> 33#include <errno.h> 34#include <grp.h> 35#include <stdio.h> 36#include <stdlib.h> 37#include <string.h> 38#include <stringlist.h> 39#include <unistd.h> 40#include "testutil.h" 41 42enum test_methods { 43 TEST_GETGRENT, 44 TEST_GETGRNAM, 45 TEST_GETGRGID, 46 TEST_GETGRENT_2PASS, 47 TEST_BUILD_SNAPSHOT 48}; 49 50static int debug = 0; 51static enum test_methods method = TEST_BUILD_SNAPSHOT; 52 53DECLARE_TEST_DATA(group) 54DECLARE_TEST_FILE_SNAPSHOT(group) 55DECLARE_1PASS_TEST(group) 56DECLARE_2PASS_TEST(group) 57 58static void clone_group(struct group *, struct group const *); 59static int compare_group(struct group *, struct group *, void *); 60static void dump_group(struct group *); 61static void free_group(struct group *); 62 63static void sdump_group(struct group *, char *, size_t); 64static int group_read_snapshot_func(struct group *, char *); 65 66static int group_check_ambiguity(struct group_test_data *, 67 struct group *); 68static int group_fill_test_data(struct group_test_data *); 69static int group_test_correctness(struct group *, void *); 70static int group_test_getgrnam(struct group *, void *); 71static int group_test_getgrgid(struct group *, void *); 72static int group_test_getgrent(struct group *, void *); 73 74static void usage(void) __attribute__((__noreturn__)); 75 76IMPLEMENT_TEST_DATA(group) 77IMPLEMENT_TEST_FILE_SNAPSHOT(group) 78IMPLEMENT_1PASS_TEST(group) 79IMPLEMENT_2PASS_TEST(group) 80 81static void 82clone_group(struct group *dest, struct group const *src) 83{ 84 assert(dest != NULL); 85 assert(src != NULL); 86 87 char **cp; 88 int members_num; 89 90 memset(dest, 0, sizeof(struct group)); 91 92 if (src->gr_name != NULL) { 93 dest->gr_name = strdup(src->gr_name); 94 assert(dest->gr_name != NULL); 95 } 96 97 if (src->gr_passwd != NULL) { 98 dest->gr_passwd = strdup(src->gr_passwd); 99 assert(dest->gr_passwd != NULL); 100 } 101 dest->gr_gid = src->gr_gid; 102 103 if (src->gr_mem != NULL) { 104 members_num = 0; 105 for (cp = src->gr_mem; *cp; ++cp) 106 ++members_num; 107 108 dest->gr_mem = (char **)malloc( 109 (members_num + 1) * (sizeof(char *))); 110 assert(dest->gr_mem != NULL); 111 memset(dest->gr_mem, 0, (members_num+1) * (sizeof(char *))); 112 113 for (cp = src->gr_mem; *cp; ++cp) { 114 dest->gr_mem[cp - src->gr_mem] = strdup(*cp); 115 assert(dest->gr_mem[cp - src->gr_mem] != NULL); 116 } 117 } 118} 119 120static void 121free_group(struct group *grp) 122{ 123 char **cp; 124 125 assert(grp != NULL); 126 127 free(grp->gr_name); 128 free(grp->gr_passwd); 129 130 for (cp = grp->gr_mem; *cp; ++cp) 131 free(*cp); 132 free(grp->gr_mem); 133} 134 135static int 136compare_group(struct group *grp1, struct group *grp2, void *mdata) 137{ 138 char **c1, **c2; 139 140 if (grp1 == grp2) 141 return (0); 142 143 if ((grp1 == NULL) || (grp2 == NULL)) 144 goto errfin; 145 146 if ((strcmp(grp1->gr_name, grp2->gr_name) != 0) || 147 (strcmp(grp1->gr_passwd, grp2->gr_passwd) != 0) || 148 (grp1->gr_gid != grp2->gr_gid)) 149 goto errfin; 150 151 c1 = grp1->gr_mem; 152 c2 = grp2->gr_mem; 153 154 if ((grp1->gr_mem == NULL) || (grp2->gr_mem == NULL)) 155 goto errfin; 156 157 for (;*c1 && *c2; ++c1, ++c2) 158 if (strcmp(*c1, *c2) != 0) 159 goto errfin; 160 161 if ((*c1 != '\0') || (*c2 != '\0')) 162 goto errfin; 163 164 return 0; 165 166errfin: 167 if ((debug) && (mdata == NULL)) { 168 printf("following structures are not equal:\n"); 169 dump_group(grp1); 170 dump_group(grp2); 171 } 172 173 return (-1); 174} 175 176static void 177sdump_group(struct group *grp, char *buffer, size_t buflen) 178{ 179 char **cp; 180 int written; 181 182 written = snprintf(buffer, buflen, "%s %s %d", 183 grp->gr_name, grp->gr_passwd, grp->gr_gid); 184 buffer += written; 185 if (written > buflen) 186 return; 187 buflen -= written; 188 189 if (grp->gr_mem != NULL) { 190 if (*(grp->gr_mem) != '\0') { 191 for (cp = grp->gr_mem; *cp; ++cp) { 192 written = snprintf(buffer, buflen, " %s",*cp); 193 buffer += written; 194 if (written > buflen) 195 return; 196 buflen -= written; 197 198 if (buflen == 0) 199 return; 200 } 201 } else 202 snprintf(buffer, buflen, " nomem"); 203 } else 204 snprintf(buffer, buflen, " (null)"); 205} 206 207static int 208group_read_snapshot_func(struct group *grp, char *line) 209{ 210 StringList *sl; 211 char *s, *ps, *ts; 212 int i; 213 214 if (debug) 215 printf("1 line read from snapshot:\n%s\n", line); 216 217 i = 0; 218 sl = NULL; 219 ps = line; 220 memset(grp, 0, sizeof(struct group)); 221 while ( (s = strsep(&ps, " ")) != NULL) { 222 switch (i) { 223 case 0: 224 grp->gr_name = strdup(s); 225 assert(grp->gr_name != NULL); 226 break; 227 228 case 1: 229 grp->gr_passwd = strdup(s); 230 assert(grp->gr_passwd != NULL); 231 break; 232 233 case 2: 234 grp->gr_gid = (gid_t)strtol(s, &ts, 10); 235 if (*ts != '\0') { 236 free(grp->gr_name); 237 free(grp->gr_passwd); 238 return (-1); 239 } 240 break; 241 242 default: 243 if (sl == NULL) { 244 if (strcmp(s, "(null)") == 0) 245 return (0); 246 247 sl = sl_init(); 248 assert(sl != NULL); 249 250 if (strcmp(s, "nomem") != 0) { 251 ts = strdup(s); 252 assert(ts != NULL); 253 sl_add(sl, ts); 254 } 255 } else { 256 ts = strdup(s); 257 assert(ts != NULL); 258 sl_add(sl, ts); 259 } 260 break; 261 }; 262 ++i; 263 } 264 265 if (i < 3) { 266 free(grp->gr_name); 267 free(grp->gr_passwd); 268 memset(grp, 0, sizeof(struct group)); 269 return (-1); 270 } 271 272 sl_add(sl, NULL); 273 grp->gr_mem = sl->sl_str; 274 275 /* NOTE: is it a dirty hack or not? */ 276 free(sl); 277 return (0); 278} 279 280static void 281dump_group(struct group *result) 282{ 283 if (result != NULL) { 284 char buffer[1024]; 285 sdump_group(result, buffer, sizeof(buffer)); 286 printf("%s\n", buffer); 287 } else 288 printf("(null)\n"); 289} 290 291static int 292group_fill_test_data(struct group_test_data *td) 293{ 294 struct group *grp; 295 296 setgroupent(1); 297 while ((grp = getgrent()) != NULL) { 298 if (group_test_correctness(grp, NULL) == 0) 299 TEST_DATA_APPEND(group, td, grp); 300 else 301 return (-1); 302 } 303 endgrent(); 304 305 return (0); 306} 307 308static int 309group_test_correctness(struct group *grp, void *mdata) 310{ 311 if (debug) { 312 printf("testing correctness with the following data:\n"); 313 dump_group(grp); 314 } 315 316 if (grp == NULL) 317 goto errfin; 318 319 if (grp->gr_name == NULL) 320 goto errfin; 321 322 if (grp->gr_passwd == NULL) 323 goto errfin; 324 325 if (grp->gr_mem == NULL) 326 goto errfin; 327 328 if (debug) 329 printf("correct\n"); 330 331 return (0); 332errfin: 333 if (debug) 334 printf("incorrect\n"); 335 336 return (-1); 337} 338 339/* group_check_ambiguity() is needed here because when doing the getgrent() 340 * calls sequence, records from different nsswitch sources can be different, 341 * though having the same pw_name/pw_uid */ 342static int 343group_check_ambiguity(struct group_test_data *td, struct group *pwd) 344{ 345 346 return (TEST_DATA_FIND(group, td, pwd, compare_group, 347 NULL) != NULL ? 0 : -1); 348} 349 350static int 351group_test_getgrnam(struct group *grp_model, void *mdata) 352{ 353 struct group *grp; 354 355 if (debug) { 356 printf("testing getgrnam() with the following data:\n"); 357 dump_group(grp_model); 358 } 359 360 grp = getgrnam(grp_model->gr_name); 361 if (group_test_correctness(grp, NULL) != 0) 362 goto errfin; 363 364 if ((compare_group(grp, grp_model, NULL) != 0) && 365 (group_check_ambiguity((struct group_test_data *)mdata, grp) 366 !=0)) 367 goto errfin; 368 369 if (debug) 370 printf("ok\n"); 371 return (0); 372 373errfin: 374 if (debug) 375 printf("not ok\n"); 376 377 return (-1); 378} 379 380static int 381group_test_getgrgid(struct group *grp_model, void *mdata) 382{ 383 struct group *grp; 384 385 if (debug) { 386 printf("testing getgrgid() with the following data...\n"); 387 dump_group(grp_model); 388 } 389 390 grp = getgrgid(grp_model->gr_gid); 391 if ((group_test_correctness(grp, NULL) != 0) || 392 ((compare_group(grp, grp_model, NULL) != 0) && 393 (group_check_ambiguity((struct group_test_data *)mdata, grp) 394 != 0))) { 395 if (debug) 396 printf("not ok\n"); 397 return (-1); 398 } else { 399 if (debug) 400 printf("ok\n"); 401 return (0); 402 } 403} 404 405static int 406group_test_getgrent(struct group *grp, void *mdata) 407{ 408 /* Only correctness can be checked when doing 1-pass test for 409 * getgrent(). */ 410 return (group_test_correctness(grp, NULL)); 411} 412 413static void 414usage(void) 415{ 416 (void)fprintf(stderr, 417 "Usage: %s -nge2 [-d] [-s <file>]\n", 418 getprogname()); 419 exit(1); 420} 421 422int 423main(int argc, char **argv) 424{ 425 struct group_test_data td, td_snap, td_2pass; 426 char *snapshot_file; 427 int rv; 428 int c; 429 430 if (argc < 2) 431 usage(); 432 433 snapshot_file = NULL; 434 while ((c = getopt(argc, argv, "nge2ds:")) != -1) 435 switch (c) { 436 case 'd': 437 debug++; 438 break; 439 case 'n': 440 method = TEST_GETGRNAM; 441 break; 442 case 'g': 443 method = TEST_GETGRGID; 444 break; 445 case 'e': 446 method = TEST_GETGRENT; 447 break; 448 case '2': 449 method = TEST_GETGRENT_2PASS; 450 break; 451 case 's': 452 snapshot_file = strdup(optarg); 453 break; 454 default: 455 usage(); 456 } 457 458 TEST_DATA_INIT(group, &td, clone_group, free_group); 459 TEST_DATA_INIT(group, &td_snap, clone_group, free_group); 460 if (snapshot_file != NULL) { 461 if (access(snapshot_file, W_OK | R_OK) != 0) { 462 if (errno == ENOENT) 463 method = TEST_BUILD_SNAPSHOT; 464 else { 465 if (debug) 466 printf("can't access the file %s\n", 467 snapshot_file); 468 469 rv = -1; 470 goto fin; 471 } 472 } else { 473 if (method == TEST_BUILD_SNAPSHOT) { 474 rv = 0; 475 goto fin; 476 } 477 478 TEST_SNAPSHOT_FILE_READ(group, snapshot_file, 479 &td_snap, group_read_snapshot_func); 480 } 481 } 482 483 rv = group_fill_test_data(&td); 484 if (rv == -1) 485 return (-1); 486 switch (method) { 487 case TEST_GETGRNAM: 488 if (snapshot_file == NULL) 489 rv = DO_1PASS_TEST(group, &td, 490 group_test_getgrnam, (void *)&td); 491 else 492 rv = DO_1PASS_TEST(group, &td_snap, 493 group_test_getgrnam, (void *)&td_snap); 494 break; 495 case TEST_GETGRGID: 496 if (snapshot_file == NULL) 497 rv = DO_1PASS_TEST(group, &td, 498 group_test_getgrgid, (void *)&td); 499 else 500 rv = DO_1PASS_TEST(group, &td_snap, 501 group_test_getgrgid, (void *)&td_snap); 502 break; 503 case TEST_GETGRENT: 504 if (snapshot_file == NULL) 505 rv = DO_1PASS_TEST(group, &td, group_test_getgrent, 506 (void *)&td); 507 else 508 rv = DO_2PASS_TEST(group, &td, &td_snap, 509 compare_group, NULL); 510 break; 511 case TEST_GETGRENT_2PASS: 512 TEST_DATA_INIT(group, &td_2pass, clone_group, free_group); 513 rv = group_fill_test_data(&td_2pass); 514 if (rv != -1) 515 rv = DO_2PASS_TEST(group, &td, &td_2pass, 516 compare_group, NULL); 517 TEST_DATA_DESTROY(group, &td_2pass); 518 break; 519 case TEST_BUILD_SNAPSHOT: 520 if (snapshot_file != NULL) 521 rv = TEST_SNAPSHOT_FILE_WRITE(group, snapshot_file, &td, 522 sdump_group); 523 break; 524 default: 525 rv = 0; 526 break; 527 }; 528 529fin: 530 TEST_DATA_DESTROY(group, &td_snap); 531 TEST_DATA_DESTROY(group, &td); 532 free(snapshot_file); 533 return (rv); 534} 535