gr_util.c revision 243328
1178431Sscf/*- 2178431Sscf * Copyright (c) 2008 Sean C. Farley <scf@FreeBSD.org> 3178431Sscf * All rights reserved. 4178431Sscf * 5178431Sscf * Redistribution and use in source and binary forms, with or without 6178431Sscf * modification, are permitted provided that the following conditions 7178431Sscf * are met: 8178431Sscf * 1. Redistributions of source code must retain the above copyright 9178431Sscf * notice, this list of conditions and the following disclaimer, 10178431Sscf * without modification, immediately at the beginning of the file. 11178431Sscf * 2. Redistributions in binary form must reproduce the above copyright 12178431Sscf * notice, this list of conditions and the following disclaimer in the 13178431Sscf * documentation and/or other materials provided with the distribution. 14178431Sscf * 15178431Sscf * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16178431Sscf * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17178431Sscf * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18178431Sscf * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19178431Sscf * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20178431Sscf * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21178431Sscf * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22178431Sscf * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23178431Sscf * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24178431Sscf * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25178431Sscf */ 26178431Sscf 27178431Sscf#include <sys/cdefs.h> 28178431Sscf__FBSDID("$FreeBSD: head/lib/libutil/gr_util.c 243328 2012-11-20 07:22:07Z bapt $"); 29178431Sscf 30178431Sscf#include <sys/param.h> 31228545Sbapt#include <sys/errno.h> 32228545Sbapt#include <sys/stat.h> 33184831Sscf 34228545Sbapt#include <ctype.h> 35228545Sbapt#include <err.h> 36228545Sbapt#include <fcntl.h> 37178431Sscf#include <grp.h> 38178431Sscf#include <inttypes.h> 39184831Sscf#include <libutil.h> 40228545Sbapt#include <paths.h> 41178431Sscf#include <stdbool.h> 42178431Sscf#include <stdio.h> 43178431Sscf#include <stdlib.h> 44178431Sscf#include <string.h> 45228545Sbapt#include <unistd.h> 46178431Sscf 47185237Sscfstruct group_storage { 48185237Sscf struct group gr; 49185237Sscf char *members[]; 50185237Sscf}; 51178431Sscf 52228545Sbaptstatic int lockfd = -1; 53228545Sbaptstatic char group_dir[PATH_MAX]; 54228545Sbaptstatic char group_file[PATH_MAX]; 55228545Sbaptstatic char tempname[PATH_MAX]; 56228545Sbaptstatic int initialized; 57228545Sbapt 58185237Sscfstatic const char group_line_format[] = "%s:%s:%ju:"; 59185237Sscf 60178431Sscf/* 61228545Sbapt * Initialize statics 62228545Sbapt */ 63228545Sbaptint 64228545Sbaptgr_init(const char *dir, const char *group) 65228545Sbapt{ 66242319Sbapt 67228545Sbapt if (dir == NULL) { 68228545Sbapt strcpy(group_dir, _PATH_ETC); 69228545Sbapt } else { 70228545Sbapt if (strlen(dir) >= sizeof(group_dir)) { 71228545Sbapt errno = ENAMETOOLONG; 72228545Sbapt return (-1); 73228545Sbapt } 74228545Sbapt strcpy(group_dir, dir); 75228545Sbapt } 76228545Sbapt 77228545Sbapt if (group == NULL) { 78228545Sbapt if (dir == NULL) { 79228545Sbapt strcpy(group_file, _PATH_GROUP); 80228545Sbapt } else if (snprintf(group_file, sizeof(group_file), "%s/group", 81228545Sbapt group_dir) > (int)sizeof(group_file)) { 82228545Sbapt errno = ENAMETOOLONG; 83228545Sbapt return (-1); 84228545Sbapt } 85228545Sbapt } else { 86228545Sbapt if (strlen(group) >= sizeof(group_file)) { 87228545Sbapt errno = ENAMETOOLONG; 88228545Sbapt return (-1); 89228545Sbapt } 90228545Sbapt strcpy(group_file, group); 91228545Sbapt } 92242319Sbapt 93228545Sbapt initialized = 1; 94228545Sbapt return (0); 95228545Sbapt} 96228545Sbapt 97228545Sbapt/* 98228545Sbapt * Lock the group file 99228545Sbapt */ 100228545Sbaptint 101228545Sbaptgr_lock(void) 102228545Sbapt{ 103228545Sbapt if (*group_file == '\0') 104228545Sbapt return (-1); 105228545Sbapt 106228545Sbapt for (;;) { 107228545Sbapt struct stat st; 108228545Sbapt 109228545Sbapt lockfd = open(group_file, O_RDONLY, 0); 110228545Sbapt if (lockfd < 0 || fcntl(lockfd, F_SETFD, 1) == -1) 111228545Sbapt err(1, "%s", group_file); 112228545Sbapt if (flock(lockfd, LOCK_EX|LOCK_NB) == -1) { 113228545Sbapt if (errno == EWOULDBLOCK) { 114228545Sbapt errx(1, "the group file is busy"); 115228545Sbapt } else { 116228545Sbapt err(1, "could not lock the group file: "); 117228545Sbapt } 118228545Sbapt } 119228545Sbapt if (fstat(lockfd, &st) == -1) 120228545Sbapt err(1, "fstat() failed: "); 121228545Sbapt if (st.st_nlink != 0) 122228545Sbapt break; 123228545Sbapt close(lockfd); 124228545Sbapt lockfd = -1; 125228545Sbapt } 126228545Sbapt return (lockfd); 127228545Sbapt} 128228545Sbapt 129228545Sbapt/* 130228545Sbapt * Create and open a presmuably safe temp file for editing group data 131228545Sbapt */ 132228545Sbaptint 133228545Sbaptgr_tmp(int mfd) 134228545Sbapt{ 135228545Sbapt char buf[8192]; 136228545Sbapt ssize_t nr; 137228545Sbapt const char *p; 138228545Sbapt int tfd; 139228545Sbapt 140228545Sbapt if (*group_file == '\0') 141228545Sbapt return (-1); 142228545Sbapt if ((p = strrchr(group_file, '/'))) 143228545Sbapt ++p; 144228545Sbapt else 145228545Sbapt p = group_file; 146228545Sbapt if (snprintf(tempname, sizeof(tempname), "%.*sgroup.XXXXXX", 147228545Sbapt (int)(p - group_file), group_file) >= (int)sizeof(tempname)) { 148228545Sbapt errno = ENAMETOOLONG; 149228545Sbapt return (-1); 150228545Sbapt } 151228545Sbapt if ((tfd = mkstemp(tempname)) == -1) 152228545Sbapt return (-1); 153228545Sbapt if (mfd != -1) { 154228545Sbapt while ((nr = read(mfd, buf, sizeof(buf))) > 0) 155228545Sbapt if (write(tfd, buf, (size_t)nr) != nr) 156228545Sbapt break; 157228545Sbapt if (nr != 0) { 158228545Sbapt unlink(tempname); 159228545Sbapt *tempname = '\0'; 160228545Sbapt close(tfd); 161228545Sbapt return (-1); 162228545Sbapt } 163228545Sbapt } 164228545Sbapt return (tfd); 165228545Sbapt} 166228545Sbapt 167228545Sbapt/* 168228545Sbapt * Copy the group file from one descriptor to another, replacing, deleting 169228545Sbapt * or adding a single record on the way. 170228545Sbapt */ 171228545Sbaptint 172228545Sbaptgr_copy(int ffd, int tfd, const struct group *gr, struct group *old_gr) 173228545Sbapt{ 174228545Sbapt char buf[8192], *end, *line, *p, *q, *r, t; 175228545Sbapt struct group *fgr; 176228545Sbapt const struct group *sgr; 177228545Sbapt size_t len; 178228545Sbapt int eof, readlen; 179228545Sbapt 180228545Sbapt sgr = gr; 181228545Sbapt if (gr == NULL) { 182228545Sbapt line = NULL; 183228545Sbapt if (old_gr == NULL) 184228545Sbapt return (-1); 185228545Sbapt sgr = old_gr; 186228545Sbapt } else if ((line = gr_make(gr)) == NULL) 187228545Sbapt return (-1); 188228545Sbapt 189228545Sbapt eof = 0; 190228545Sbapt len = 0; 191228545Sbapt p = q = end = buf; 192228545Sbapt for (;;) { 193228545Sbapt /* find the end of the current line */ 194228545Sbapt for (p = q; q < end && *q != '\0'; ++q) 195228545Sbapt if (*q == '\n') 196228545Sbapt break; 197228545Sbapt 198228545Sbapt /* if we don't have a complete line, fill up the buffer */ 199228545Sbapt if (q >= end) { 200228545Sbapt if (eof) 201228545Sbapt break; 202228545Sbapt if ((size_t)(q - p) >= sizeof(buf)) { 203228545Sbapt warnx("group line too long"); 204228545Sbapt errno = EINVAL; /* hack */ 205228545Sbapt goto err; 206228545Sbapt } 207228545Sbapt if (p < end) { 208228545Sbapt q = memmove(buf, p, end -p); 209228545Sbapt end -= p - buf; 210228545Sbapt } else { 211228545Sbapt p = q = end = buf; 212228545Sbapt } 213228545Sbapt readlen = read(ffd, end, sizeof(buf) - (end -buf)); 214228545Sbapt if (readlen == -1) 215228545Sbapt goto err; 216228545Sbapt else 217228545Sbapt len = (size_t)readlen; 218228545Sbapt if (len == 0 && p == buf) 219228545Sbapt break; 220228545Sbapt end += len; 221228545Sbapt len = end - buf; 222228545Sbapt if (len < (ssize_t)sizeof(buf)) { 223228545Sbapt eof = 1; 224228545Sbapt if (len > 0 && buf[len -1] != '\n') 225228545Sbapt ++len, *end++ = '\n'; 226228545Sbapt } 227228545Sbapt continue; 228228545Sbapt } 229228545Sbapt 230228545Sbapt /* is it a blank line or a comment? */ 231228545Sbapt for (r = p; r < q && isspace(*r); ++r) 232228545Sbapt /* nothing */; 233228545Sbapt if (r == q || *r == '#') { 234228545Sbapt /* yep */ 235228545Sbapt if (write(tfd, p, q -p + 1) != q - p + 1) 236228545Sbapt goto err; 237228545Sbapt ++q; 238228545Sbapt continue; 239228545Sbapt } 240228545Sbapt 241228545Sbapt /* is it the one we're looking for? */ 242228545Sbapt 243228545Sbapt t = *q; 244228545Sbapt *q = '\0'; 245228545Sbapt 246228545Sbapt fgr = gr_scan(r); 247228545Sbapt 248228545Sbapt /* fgr is either a struct group for the current line, 249228545Sbapt * or NULL if the line is malformed. 250228545Sbapt */ 251228545Sbapt 252228545Sbapt *q = t; 253228545Sbapt if (fgr == NULL || fgr->gr_gid != sgr->gr_gid) { 254228545Sbapt /* nope */ 255228545Sbapt if (fgr != NULL) 256228545Sbapt free(fgr); 257228545Sbapt if (write(tfd, p, q - p + 1) != q - p + 1) 258228545Sbapt goto err; 259228545Sbapt ++q; 260228545Sbapt continue; 261228545Sbapt } 262228545Sbapt if (old_gr && !gr_equal(fgr, old_gr)) { 263228545Sbapt warnx("entry inconsistent"); 264228545Sbapt free(fgr); 265228545Sbapt errno = EINVAL; /* hack */ 266228545Sbapt goto err; 267228545Sbapt } 268228545Sbapt free(fgr); 269228545Sbapt 270228545Sbapt /* it is, replace or remove it */ 271228545Sbapt if (line != NULL) { 272228545Sbapt len = strlen(line); 273228545Sbapt if (write(tfd, line, len) != (int) len) 274228545Sbapt goto err; 275228545Sbapt } else { 276228545Sbapt /* when removed, avoid the \n */ 277228545Sbapt q++; 278228545Sbapt } 279228545Sbapt /* we're done, just copy the rest over */ 280228545Sbapt for (;;) { 281228545Sbapt if (write(tfd, q, end - q) != end - q) 282228545Sbapt goto err; 283228545Sbapt q = buf; 284228545Sbapt readlen = read(ffd, buf, sizeof(buf)); 285228545Sbapt if (readlen == 0) 286228545Sbapt break; 287228545Sbapt else 288228545Sbapt len = (size_t)readlen; 289228545Sbapt if (readlen == -1) 290228545Sbapt goto err; 291228545Sbapt end = buf + len; 292228545Sbapt } 293228545Sbapt goto done; 294228545Sbapt } 295228545Sbapt 296228545Sbapt /* if we got here, we didn't find the old entry */ 297228545Sbapt if (line == NULL) { 298228545Sbapt errno = ENOENT; 299228545Sbapt goto err; 300228545Sbapt } 301228545Sbapt len = strlen(line); 302228545Sbapt if ((size_t)write(tfd, line, len) != len || 303228545Sbapt write(tfd, "\n", 1) != 1) 304228545Sbapt goto err; 305228545Sbapt done: 306228545Sbapt if (line != NULL) 307228545Sbapt free(line); 308228545Sbapt return (0); 309228545Sbapt err: 310228545Sbapt if (line != NULL) 311228545Sbapt free(line); 312228545Sbapt return (-1); 313228545Sbapt} 314228545Sbapt 315228545Sbapt/* 316228545Sbapt * Regenerate the group file 317228545Sbapt */ 318228545Sbaptint 319228545Sbaptgr_mkdb(void) 320228545Sbapt{ 321243328Sbapt int ret; 322243328Sbapt 323243328Sbapt ret = rename(tempname, group_file); 324243328Sbapt 325243328Sbapt if (ret == 0) 326243328Sbapt chmod(group_file, 0644); 327243328Sbapt 328243328Sbapt return (ret); 329228545Sbapt} 330228545Sbapt 331228545Sbapt/* 332228545Sbapt * Clean up. Preserver errno for the caller's convenience. 333228545Sbapt */ 334228545Sbaptvoid 335228545Sbaptgr_fini(void) 336228545Sbapt{ 337228545Sbapt int serrno; 338228545Sbapt 339228545Sbapt if (!initialized) 340228545Sbapt return; 341228545Sbapt initialized = 0; 342228545Sbapt serrno = errno; 343228545Sbapt if (*tempname != '\0') { 344228545Sbapt unlink(tempname); 345228545Sbapt *tempname = '\0'; 346228545Sbapt } 347228545Sbapt if (lockfd != -1) 348228545Sbapt close(lockfd); 349228545Sbapt errno = serrno; 350228545Sbapt} 351228545Sbapt 352228545Sbapt/* 353178431Sscf * Compares two struct group's. 354178431Sscf */ 355178431Sscfint 356178431Sscfgr_equal(const struct group *gr1, const struct group *gr2) 357178431Sscf{ 358185237Sscf int gr1_ndx; 359185237Sscf int gr2_ndx; 360184831Sscf bool found; 361178431Sscf 362178431Sscf /* Check that the non-member information is the same. */ 363185237Sscf if (gr1->gr_name == NULL || gr2->gr_name == NULL) { 364185237Sscf if (gr1->gr_name != gr2->gr_name) 365185237Sscf return (false); 366185237Sscf } else if (strcmp(gr1->gr_name, gr2->gr_name) != 0) 367185237Sscf return (false); 368185237Sscf if (gr1->gr_passwd == NULL || gr2->gr_passwd == NULL) { 369185237Sscf if (gr1->gr_passwd != gr2->gr_passwd) 370185237Sscf return (false); 371185237Sscf } else if (strcmp(gr1->gr_passwd, gr2->gr_passwd) != 0) 372185237Sscf return (false); 373185237Sscf if (gr1->gr_gid != gr2->gr_gid) 374185237Sscf return (false); 375178431Sscf 376178431Sscf /* Check all members in both groups. */ 377185237Sscf if (gr1->gr_mem == NULL || gr2->gr_mem == NULL) { 378185237Sscf if (gr1->gr_mem != gr2->gr_mem) 379185237Sscf return (false); 380185237Sscf } else { 381185237Sscf for (found = false, gr1_ndx = 0; gr1->gr_mem[gr1_ndx] != NULL; 382185237Sscf gr1_ndx++) { 383185237Sscf for (gr2_ndx = 0; gr2->gr_mem[gr2_ndx] != NULL; 384185237Sscf gr2_ndx++) 385185237Sscf if (strcmp(gr1->gr_mem[gr1_ndx], 386185237Sscf gr2->gr_mem[gr2_ndx]) == 0) { 387178431Sscf found = true; 388178431Sscf break; 389178431Sscf } 390185237Sscf if (!found) 391185237Sscf return (false); 392178431Sscf } 393178431Sscf 394178431Sscf /* Check that group2 does not have more members than group1. */ 395185237Sscf if (gr2->gr_mem[gr1_ndx] != NULL) 396185237Sscf return (false); 397178431Sscf } 398178431Sscf 399185237Sscf return (true); 400178431Sscf} 401178431Sscf 402178431Sscf/* 403178431Sscf * Make a group line out of a struct group. 404178431Sscf */ 405178431Sscfchar * 406178431Sscfgr_make(const struct group *gr) 407178431Sscf{ 408178431Sscf char *line; 409185237Sscf size_t line_size; 410178431Sscf int ndx; 411178431Sscf 412178431Sscf /* Calculate the length of the group line. */ 413185237Sscf line_size = snprintf(NULL, 0, group_line_format, gr->gr_name, 414178431Sscf gr->gr_passwd, (uintmax_t)gr->gr_gid) + 1; 415185237Sscf if (gr->gr_mem != NULL) { 416185237Sscf for (ndx = 0; gr->gr_mem[ndx] != NULL; ndx++) 417185237Sscf line_size += strlen(gr->gr_mem[ndx]) + 1; 418185237Sscf if (ndx > 0) 419185237Sscf line_size--; 420185237Sscf } 421178431Sscf 422178431Sscf /* Create the group line and fill it. */ 423185237Sscf if ((line = malloc(line_size)) == NULL) 424178431Sscf return (NULL); 425200423Sscf snprintf(line, line_size, group_line_format, gr->gr_name, gr->gr_passwd, 426200423Sscf (uintmax_t)gr->gr_gid); 427185237Sscf if (gr->gr_mem != NULL) 428185237Sscf for (ndx = 0; gr->gr_mem[ndx] != NULL; ndx++) { 429185237Sscf strcat(line, gr->gr_mem[ndx]); 430185237Sscf if (gr->gr_mem[ndx + 1] != NULL) 431185237Sscf strcat(line, ","); 432185237Sscf } 433178431Sscf 434178431Sscf return (line); 435178431Sscf} 436178431Sscf 437178431Sscf/* 438178431Sscf * Duplicate a struct group. 439178431Sscf */ 440178431Sscfstruct group * 441178431Sscfgr_dup(const struct group *gr) 442178431Sscf{ 443185237Sscf char *dst; 444184831Sscf size_t len; 445185237Sscf struct group_storage *gs; 446178431Sscf int ndx; 447185237Sscf int num_mem; 448178431Sscf 449185237Sscf /* Calculate size of the group. */ 450185237Sscf len = sizeof(*gs); 451185237Sscf if (gr->gr_name != NULL) 452185237Sscf len += strlen(gr->gr_name) + 1; 453185237Sscf if (gr->gr_passwd != NULL) 454185237Sscf len += strlen(gr->gr_passwd) + 1; 455178431Sscf if (gr->gr_mem != NULL) { 456185237Sscf for (num_mem = 0; gr->gr_mem[num_mem] != NULL; num_mem++) 457185237Sscf len += strlen(gr->gr_mem[num_mem]) + 1; 458185237Sscf len += (num_mem + 1) * sizeof(*gr->gr_mem); 459185237Sscf } else 460185237Sscf num_mem = -1; 461178431Sscf 462178431Sscf /* Create new group and copy old group into it. */ 463185237Sscf if ((gs = calloc(1, len)) == NULL) 464178431Sscf return (NULL); 465185237Sscf dst = (char *)&gs->members[num_mem + 1]; 466178431Sscf if (gr->gr_name != NULL) { 467185237Sscf gs->gr.gr_name = dst; 468185237Sscf dst = stpcpy(gs->gr.gr_name, gr->gr_name) + 1; 469178431Sscf } 470178431Sscf if (gr->gr_passwd != NULL) { 471185237Sscf gs->gr.gr_passwd = dst; 472185237Sscf dst = stpcpy(gs->gr.gr_passwd, gr->gr_passwd) + 1; 473178431Sscf } 474185237Sscf gs->gr.gr_gid = gr->gr_gid; 475178431Sscf if (gr->gr_mem != NULL) { 476185237Sscf gs->gr.gr_mem = gs->members; 477185237Sscf for (ndx = 0; ndx < num_mem; ndx++) { 478185237Sscf gs->gr.gr_mem[ndx] = dst; 479185237Sscf dst = stpcpy(gs->gr.gr_mem[ndx], gr->gr_mem[ndx]) + 1; 480178431Sscf } 481185237Sscf gs->gr.gr_mem[ndx] = NULL; 482178431Sscf } 483178431Sscf 484185237Sscf return (&gs->gr); 485178431Sscf} 486178431Sscf 487178431Sscf/* 488178431Sscf * Scan a line and place it into a group structure. 489178431Sscf */ 490178431Sscfstatic bool 491178431Sscf__gr_scan(char *line, struct group *gr) 492178431Sscf{ 493178431Sscf char *loc; 494178431Sscf int ndx; 495178431Sscf 496178431Sscf /* Assign non-member information to structure. */ 497178431Sscf gr->gr_name = line; 498178431Sscf if ((loc = strchr(line, ':')) == NULL) 499178431Sscf return (false); 500178431Sscf *loc = '\0'; 501178431Sscf gr->gr_passwd = loc + 1; 502184831Sscf if (*gr->gr_passwd == ':') 503184831Sscf *gr->gr_passwd = '\0'; 504178431Sscf else { 505178431Sscf if ((loc = strchr(loc + 1, ':')) == NULL) 506178431Sscf return (false); 507178431Sscf *loc = '\0'; 508178431Sscf } 509184831Sscf if (sscanf(loc + 1, "%u", &gr->gr_gid) != 1) 510178431Sscf return (false); 511178431Sscf 512178431Sscf /* Assign member information to structure. */ 513178431Sscf if ((loc = strchr(loc + 1, ':')) == NULL) 514178431Sscf return (false); 515178431Sscf line = loc + 1; 516178431Sscf gr->gr_mem = NULL; 517185237Sscf ndx = 0; 518185237Sscf do { 519185237Sscf gr->gr_mem = reallocf(gr->gr_mem, sizeof(*gr->gr_mem) * 520185237Sscf (ndx + 1)); 521185237Sscf if (gr->gr_mem == NULL) 522185237Sscf return (false); 523185237Sscf 524185237Sscf /* Skip locations without members (i.e., empty string). */ 525178431Sscf do { 526178431Sscf gr->gr_mem[ndx] = strsep(&line, ","); 527185237Sscf } while (gr->gr_mem[ndx] != NULL && *gr->gr_mem[ndx] == '\0'); 528185237Sscf } while (gr->gr_mem[ndx++] != NULL); 529178431Sscf 530178431Sscf return (true); 531178431Sscf} 532178431Sscf 533178431Sscf/* 534178431Sscf * Create a struct group from a line. 535178431Sscf */ 536178431Sscfstruct group * 537178431Sscfgr_scan(const char *line) 538178431Sscf{ 539184831Sscf struct group gr; 540185237Sscf char *line_copy; 541185237Sscf struct group *new_gr; 542178431Sscf 543185237Sscf if ((line_copy = strdup(line)) == NULL) 544178431Sscf return (NULL); 545185237Sscf if (!__gr_scan(line_copy, &gr)) { 546185237Sscf free(line_copy); 547178431Sscf return (NULL); 548178431Sscf } 549185237Sscf new_gr = gr_dup(&gr); 550185237Sscf free(line_copy); 551178431Sscf if (gr.gr_mem != NULL) 552178431Sscf free(gr.gr_mem); 553178431Sscf 554185237Sscf return (new_gr); 555178431Sscf} 556