Annotation of embedaddon/rsync/uidlist.c, revision 1.1.1.3

1.1       misho       1: /*
                      2:  * Handle the mapping of uid/gid and user/group names between systems.
                      3:  *
                      4:  * Copyright (C) 1996 Andrew Tridgell
                      5:  * Copyright (C) 1996 Paul Mackerras
1.1.1.3 ! misho       6:  * Copyright (C) 2004-2015 Wayne Davison
1.1       misho       7:  *
                      8:  * This program is free software; you can redistribute it and/or modify
                      9:  * it under the terms of the GNU General Public License as published by
                     10:  * the Free Software Foundation; either version 3 of the License, or
                     11:  * (at your option) any later version.
                     12:  *
                     13:  * This program is distributed in the hope that it will be useful,
                     14:  * but WITHOUT ANY WARRANTY; without even the implied warranty of
                     15:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     16:  * GNU General Public License for more details.
                     17:  *
                     18:  * You should have received a copy of the GNU General Public License along
                     19:  * with this program; if not, visit the http://fsf.org website.
                     20:  */
                     21: 
                     22: /* If the source username/group does not exist on the target then use
                     23:  * the numeric IDs.  Never do any mapping for uid=0 or gid=0 as these
                     24:  * are special. */
                     25: 
                     26: #include "rsync.h"
1.1.1.2   misho      27: #include "ifuncs.h"
                     28: #include "itypes.h"
1.1       misho      29: #include "io.h"
                     30: 
                     31: extern int am_root;
                     32: extern int preserve_uid;
                     33: extern int preserve_gid;
                     34: extern int preserve_acls;
                     35: extern int numeric_ids;
1.1.1.2   misho      36: extern gid_t our_gid;
                     37: extern char *usermap;
                     38: extern char *groupmap;
1.1       misho      39: 
                     40: #ifdef HAVE_GETGROUPS
                     41: # ifndef GETGROUPS_T
                     42: #  define GETGROUPS_T gid_t
                     43: # endif
                     44: #endif
                     45: 
1.1.1.2   misho      46: #define NFLAGS_WILD_NAME_MATCH (1<<0)
                     47: #define NFLAGS_NAME_MATCH (1<<1)
                     48: 
                     49: union name_or_id {
                     50:     const char *name;
                     51:     id_t max_id;
                     52: };
                     53: 
1.1       misho      54: struct idlist {
                     55:        struct idlist *next;
1.1.1.2   misho      56:        union name_or_id u;
1.1       misho      57:        id_t id, id2;
                     58:        uint16 flags;
                     59: };
                     60: 
1.1.1.2   misho      61: static struct idlist *uidlist, *uidmap;
                     62: static struct idlist *gidlist, *gidmap;
                     63: 
                     64: static id_t id_parse(const char *num_str)
                     65: {
                     66:        id_t tmp, num = 0;
                     67:        const char *cp = num_str;
                     68: 
                     69:        while (*cp) {
                     70:                if (!isDigit(cp)) {
                     71:                  invalid_num:
                     72:                        rprintf(FERROR, "Invalid ID number: %s\n", num_str);
                     73:                        exit_cleanup(RERR_SYNTAX);
                     74:                }
                     75:                tmp = num * 10 + *cp++ - '0';
                     76:                if (tmp < num)
                     77:                        goto invalid_num;
                     78:                num = tmp;
                     79:        }
                     80: 
                     81:        return num;
                     82: }
1.1       misho      83: 
1.1.1.2   misho      84: static struct idlist *add_to_list(struct idlist **root, id_t id, union name_or_id noiu,
1.1       misho      85:                                  id_t id2, uint16 flags)
                     86: {
                     87:        struct idlist *node = new(struct idlist);
                     88:        if (!node)
                     89:                out_of_memory("add_to_list");
                     90:        node->next = *root;
1.1.1.2   misho      91:        node->u = noiu;
1.1       misho      92:        node->id = id;
                     93:        node->id2 = id2;
                     94:        node->flags = flags;
                     95:        *root = node;
                     96:        return node;
                     97: }
                     98: 
                     99: /* turn a uid into a user name */
1.1.1.2   misho     100: char *uid_to_user(uid_t uid)
1.1       misho     101: {
                    102:        struct passwd *pass = getpwuid(uid);
                    103:        if (pass)
                    104:                return strdup(pass->pw_name);
                    105:        return NULL;
                    106: }
                    107: 
                    108: /* turn a gid into a group name */
1.1.1.2   misho     109: char *gid_to_group(gid_t gid)
1.1       misho     110: {
                    111:        struct group *grp = getgrgid(gid);
                    112:        if (grp)
                    113:                return strdup(grp->gr_name);
                    114:        return NULL;
                    115: }
                    116: 
1.1.1.2   misho     117: /* Parse a user name or (optionally) a number into a uid */
                    118: int user_to_uid(const char *name, uid_t *uid_p, BOOL num_ok)
1.1       misho     119: {
1.1.1.2   misho     120:        struct passwd *pass;
                    121:        if (!name || !*name)
                    122:                return 0;
                    123:        if (num_ok && name[strspn(name, "0123456789")] == '\0') {
                    124:                *uid_p = id_parse(name);
                    125:                return 1;
                    126:        }
                    127:        if (!(pass = getpwnam(name)))
                    128:                return 0;
                    129:        *uid_p = pass->pw_uid;
                    130:        return 1;
1.1       misho     131: }
                    132: 
1.1.1.2   misho     133: /* Parse a group name or (optionally) a number into a gid */
                    134: int group_to_gid(const char *name, gid_t *gid_p, BOOL num_ok)
1.1       misho     135: {
1.1.1.2   misho     136:        struct group *grp;
                    137:        if (!name || !*name)
                    138:                return 0;
                    139:        if (num_ok && name[strspn(name, "0123456789")] == '\0') {
                    140:                *gid_p = id_parse(name);
                    141:                return 1;
                    142:        }
                    143:        if (!(grp = getgrnam(name)))
                    144:                return 0;
                    145:        *gid_p = grp->gr_gid;
                    146:        return 1;
1.1       misho     147: }
                    148: 
                    149: static int is_in_group(gid_t gid)
                    150: {
                    151: #ifdef HAVE_GETGROUPS
                    152:        static gid_t last_in;
                    153:        static int ngroups = -2, last_out = -1;
                    154:        static GETGROUPS_T *gidset;
                    155:        int n;
                    156: 
                    157:        if (gid == last_in && last_out >= 0)
                    158:                return last_out;
                    159:        if (ngroups < -1) {
                    160:                if ((ngroups = getgroups(0, NULL)) < 0)
                    161:                        ngroups = 0;
                    162:                gidset = new_array(GETGROUPS_T, ngroups+1);
                    163:                if (!gidset)
                    164:                        out_of_memory("is_in_group");
                    165:                if (ngroups > 0)
                    166:                        ngroups = getgroups(ngroups, gidset);
                    167:                /* The default gid might not be in the list on some systems. */
                    168:                for (n = 0; n < ngroups; n++) {
1.1.1.2   misho     169:                        if (gidset[n] == our_gid)
1.1       misho     170:                                break;
                    171:                }
                    172:                if (n == ngroups)
1.1.1.2   misho     173:                        gidset[ngroups++] = our_gid;
                    174:                if (DEBUG_GTE(OWN, 2)) {
1.1       misho     175:                        int pos;
                    176:                        char *gidbuf = new_array(char, ngroups*21+32);
                    177:                        if (!gidbuf)
                    178:                                out_of_memory("is_in_group");
                    179:                        pos = snprintf(gidbuf, 32, "process has %d gid%s: ",
                    180:                                       ngroups, ngroups == 1? "" : "s");
                    181:                        for (n = 0; n < ngroups; n++) {
                    182:                                pos += snprintf(gidbuf+pos, 21, " %d", (int)gidset[n]);
                    183:                        }
                    184:                        rprintf(FINFO, "%s\n", gidbuf);
                    185:                        free(gidbuf);
                    186:                }
                    187:        }
                    188: 
                    189:        last_in = gid;
                    190:        for (n = 0; n < ngroups; n++) {
                    191:                if (gidset[n] == gid)
                    192:                        return last_out = 1;
                    193:        }
                    194:        return last_out = 0;
                    195: 
                    196: #else
1.1.1.2   misho     197:        return gid == our_gid;
1.1       misho     198: #endif
                    199: }
                    200: 
1.1.1.2   misho     201: /* Add a uid/gid to its list of ids.  Only called on receiving side. */
                    202: static struct idlist *recv_add_id(struct idlist **idlist_ptr, struct idlist *idmap,
                    203:                                  id_t id, const char *name)
1.1       misho     204: {
                    205:        struct idlist *node;
1.1.1.2   misho     206:        union name_or_id noiu;
                    207:        int flag;
                    208:        id_t id2;
1.1       misho     209: 
1.1.1.2   misho     210:        noiu.name = name; /* ensure that add_to_list() gets the raw value. */
                    211:        if (!name)
                    212:                name = "";
1.1       misho     213: 
1.1.1.2   misho     214:        for (node = idmap; node; node = node->next) {
                    215:                if (node->flags & NFLAGS_WILD_NAME_MATCH) {
                    216:                        if (!wildmatch(node->u.name, name))
                    217:                                continue;
                    218:                } else if (node->flags & NFLAGS_NAME_MATCH) {
                    219:                        if (strcmp(node->u.name, name) != 0)
                    220:                                continue;
                    221:                } else if (node->u.max_id) {
                    222:                        if (id < node->id || id > node->u.max_id)
                    223:                                continue;
                    224:                } else {
                    225:                        if (node->id != id)
                    226:                                continue;
                    227:                }
                    228:                break;
1.1       misho     229:        }
1.1.1.2   misho     230:        if (node)
                    231:                id2 = node->id2;
                    232:        else if (*name && id) {
                    233:                if (idlist_ptr == &uidlist) {
                    234:                        uid_t uid;
                    235:                        id2 = user_to_uid(name, &uid, False) ? uid : id;
                    236:                } else {
                    237:                        gid_t gid;
                    238:                        id2 = group_to_gid(name, &gid, False) ? gid : id;
                    239:                }
                    240:        } else
                    241:                id2 = id;
1.1       misho     242: 
1.1.1.2   misho     243:        flag = idlist_ptr == &gidlist && !am_root && !is_in_group(id2) ? FLAG_SKIP_GROUP : 0;
                    244:        node = add_to_list(idlist_ptr, id, noiu, id2, flag);
1.1       misho     245: 
1.1.1.2   misho     246:        if (DEBUG_GTE(OWN, 2)) {
                    247:                rprintf(FINFO, "%sid %u(%s) maps to %u\n",
                    248:                        idlist_ptr == &uidlist ? "u" : "g",
                    249:                        (unsigned)id, name, (unsigned)id2);
1.1       misho     250:        }
                    251: 
                    252:        return node;
                    253: }
                    254: 
                    255: /* this function is a definate candidate for a faster algorithm */
                    256: uid_t match_uid(uid_t uid)
                    257: {
1.1.1.2   misho     258:        static struct idlist *last = NULL;
1.1       misho     259:        struct idlist *list;
                    260: 
1.1.1.2   misho     261:        if (last && uid == last->id)
                    262:                return last->id2;
1.1       misho     263: 
                    264:        for (list = uidlist; list; list = list->next) {
                    265:                if (list->id == uid)
1.1.1.2   misho     266:                        break;
1.1       misho     267:        }
                    268: 
1.1.1.2   misho     269:        if (!list)
                    270:                list = recv_add_id(&uidlist, uidmap, uid, NULL);
                    271:        last = list;
                    272: 
                    273:        return list->id2;
1.1       misho     274: }
                    275: 
                    276: gid_t match_gid(gid_t gid, uint16 *flags_ptr)
                    277: {
                    278:        static struct idlist *last = NULL;
                    279:        struct idlist *list;
                    280: 
                    281:        if (last && gid == last->id)
                    282:                list = last;
                    283:        else {
                    284:                for (list = gidlist; list; list = list->next) {
                    285:                        if (list->id == gid)
                    286:                                break;
                    287:                }
                    288:                if (!list)
1.1.1.2   misho     289:                        list = recv_add_id(&gidlist, gidmap, gid, NULL);
1.1       misho     290:                last = list;
                    291:        }
                    292: 
                    293:        if (flags_ptr && list->flags & FLAG_SKIP_GROUP)
                    294:                *flags_ptr |= FLAG_SKIP_GROUP;
                    295:        return list->id2;
                    296: }
                    297: 
                    298: /* Add a uid to the list of uids.  Only called on sending side. */
                    299: const char *add_uid(uid_t uid)
                    300: {
                    301:        struct idlist *list;
                    302:        struct idlist *node;
1.1.1.2   misho     303:        union name_or_id noiu;
1.1       misho     304: 
                    305:        if (uid == 0)   /* don't map root */
                    306:                return NULL;
                    307: 
                    308:        for (list = uidlist; list; list = list->next) {
                    309:                if (list->id == uid)
                    310:                        return NULL;
                    311:        }
                    312: 
1.1.1.2   misho     313:        noiu.name = uid_to_user(uid);
                    314:        node = add_to_list(&uidlist, uid, noiu, 0, 0);
                    315:        return node->u.name;
1.1       misho     316: }
                    317: 
                    318: /* Add a gid to the list of gids.  Only called on sending side. */
                    319: const char *add_gid(gid_t gid)
                    320: {
                    321:        struct idlist *list;
                    322:        struct idlist *node;
1.1.1.2   misho     323:        union name_or_id noiu;
1.1       misho     324: 
                    325:        if (gid == 0)   /* don't map root */
                    326:                return NULL;
                    327: 
                    328:        for (list = gidlist; list; list = list->next) {
                    329:                if (list->id == gid)
                    330:                        return NULL;
                    331:        }
                    332: 
1.1.1.2   misho     333:        noiu.name = gid_to_group(gid);
                    334:        node = add_to_list(&gidlist, gid, noiu, 0, 0);
                    335:        return node->u.name;
1.1       misho     336: }
                    337: 
                    338: /* send a complete uid/gid mapping to the peer */
                    339: void send_id_list(int f)
                    340: {
                    341:        struct idlist *list;
                    342: 
                    343:        if (preserve_uid || preserve_acls) {
                    344:                int len;
                    345:                /* we send sequences of uid/byte-length/name */
                    346:                for (list = uidlist; list; list = list->next) {
1.1.1.2   misho     347:                        if (!list->u.name)
1.1       misho     348:                                continue;
1.1.1.2   misho     349:                        len = strlen(list->u.name);
1.1       misho     350:                        write_varint30(f, list->id);
                    351:                        write_byte(f, len);
1.1.1.2   misho     352:                        write_buf(f, list->u.name, len);
1.1       misho     353:                }
                    354: 
                    355:                /* terminate the uid list with a 0 uid. We explicitly exclude
                    356:                 * 0 from the list */
                    357:                write_varint30(f, 0);
                    358:        }
                    359: 
                    360:        if (preserve_gid || preserve_acls) {
                    361:                int len;
                    362:                for (list = gidlist; list; list = list->next) {
1.1.1.2   misho     363:                        if (!list->u.name)
1.1       misho     364:                                continue;
1.1.1.2   misho     365:                        len = strlen(list->u.name);
1.1       misho     366:                        write_varint30(f, list->id);
                    367:                        write_byte(f, len);
1.1.1.2   misho     368:                        write_buf(f, list->u.name, len);
1.1       misho     369:                }
                    370:                write_varint30(f, 0);
                    371:        }
                    372: }
                    373: 
                    374: uid_t recv_user_name(int f, uid_t uid)
                    375: {
                    376:        struct idlist *node;
                    377:        int len = read_byte(f);
                    378:        char *name = new_array(char, len+1);
                    379:        if (!name)
                    380:                out_of_memory("recv_user_name");
                    381:        read_sbuf(f, name, len);
                    382:        if (numeric_ids < 0) {
                    383:                free(name);
                    384:                name = NULL;
                    385:        }
1.1.1.2   misho     386:        node = recv_add_id(&uidlist, uidmap, uid, name); /* node keeps name's memory */
1.1       misho     387:        return node->id2;
                    388: }
                    389: 
                    390: gid_t recv_group_name(int f, gid_t gid, uint16 *flags_ptr)
                    391: {
                    392:        struct idlist *node;
                    393:        int len = read_byte(f);
                    394:        char *name = new_array(char, len+1);
                    395:        if (!name)
                    396:                out_of_memory("recv_group_name");
                    397:        read_sbuf(f, name, len);
                    398:        if (numeric_ids < 0) {
                    399:                free(name);
                    400:                name = NULL;
                    401:        }
1.1.1.2   misho     402:        node = recv_add_id(&gidlist, gidmap, gid, name); /* node keeps name's memory */
1.1       misho     403:        if (flags_ptr && node->flags & FLAG_SKIP_GROUP)
                    404:                *flags_ptr |= FLAG_SKIP_GROUP;
                    405:        return node->id2;
                    406: }
                    407: 
                    408: /* recv a complete uid/gid mapping from the peer and map the uid/gid
                    409:  * in the file list to local names */
                    410: void recv_id_list(int f, struct file_list *flist)
                    411: {
                    412:        id_t id;
                    413:        int i;
                    414: 
                    415:        if ((preserve_uid || preserve_acls) && numeric_ids <= 0) {
                    416:                /* read the uid list */
                    417:                while ((id = read_varint30(f)) != 0)
                    418:                        recv_user_name(f, id);
                    419:        }
                    420: 
                    421:        if ((preserve_gid || preserve_acls) && numeric_ids <= 0) {
                    422:                /* read the gid list */
                    423:                while ((id = read_varint30(f)) != 0)
                    424:                        recv_group_name(f, id, NULL);
                    425:        }
                    426: 
                    427:        /* Now convert all the uids/gids from sender values to our values. */
                    428: #ifdef SUPPORT_ACLS
1.1.1.2   misho     429:        if (preserve_acls && (!numeric_ids || usermap || groupmap))
1.1       misho     430:                match_acl_ids();
                    431: #endif
1.1.1.2   misho     432:        if (am_root && preserve_uid && (!numeric_ids || usermap)) {
1.1       misho     433:                for (i = 0; i < flist->used; i++)
                    434:                        F_OWNER(flist->files[i]) = match_uid(F_OWNER(flist->files[i]));
                    435:        }
1.1.1.2   misho     436:        if (preserve_gid && (!am_root || !numeric_ids || groupmap)) {
1.1       misho     437:                for (i = 0; i < flist->used; i++) {
                    438:                        F_GROUP(flist->files[i]) = match_gid(F_GROUP(flist->files[i]),
                    439:                                                             &flist->files[i]->flags);
                    440:                }
                    441:        }
                    442: }
1.1.1.2   misho     443: 
                    444: void parse_name_map(char *map, BOOL usernames)
                    445: {
                    446:        struct idlist **idmap_ptr = usernames ? &uidmap : &gidmap;
                    447:        struct idlist **idlist_ptr = usernames ? &uidlist : &gidlist;
                    448:        char *colon, *cp = map + strlen(map);
                    449:        union name_or_id noiu;
                    450:        id_t id1;
                    451:        uint16 flags;
                    452: 
                    453:        /* Parse the list in reverse, so the order in the struct is right. */
                    454:        while (1) {
                    455:                while (cp > map && cp[-1] != ',') cp--;
                    456:                if (!(colon = strchr(cp, ':'))) {
                    457:                        rprintf(FERROR, "No colon found in --%smap: %s\n",
                    458:                                usernames ? "user" : "group", cp);
                    459:                        exit_cleanup(RERR_SYNTAX);
                    460:                }
                    461:                if (!colon[1]) {
                    462:                        rprintf(FERROR, "No name found after colon --%smap: %s\n",
                    463:                                usernames ? "user" : "group", cp);
                    464:                        exit_cleanup(RERR_SYNTAX);
                    465:                }
                    466:                *colon = '\0';
                    467: 
                    468:                if (isDigit(cp)) {
                    469:                        char *dash = strchr(cp, '-');
                    470:                        if (strspn(cp, "0123456789-") != (size_t)(colon - cp)
                    471:                         || (dash && (!dash[1] || strchr(dash+1, '-')))) {
                    472:                                rprintf(FERROR, "Invalid number in --%smap: %s\n",
                    473:                                        usernames ? "user" : "group", cp);
                    474:                                exit_cleanup(RERR_SYNTAX);
                    475:                        }
1.1.1.3 ! misho     476:                        if (dash) {
        !           477:                                *dash = '\0';
1.1.1.2   misho     478:                                noiu.max_id = id_parse(dash+1);
1.1.1.3 ! misho     479:                        } else
1.1.1.2   misho     480:                                noiu.max_id = 0;
                    481:                        flags = 0;
                    482:                        id1 = id_parse(cp);
1.1.1.3 ! misho     483:                        if (dash)
        !           484:                                *dash = '-';
1.1.1.2   misho     485:                } else if (strpbrk(cp, "*[?")) {
                    486:                        flags = NFLAGS_WILD_NAME_MATCH;
                    487:                        noiu.name = cp;
                    488:                        id1 = 0;
                    489:                } else {
                    490:                        flags = NFLAGS_NAME_MATCH;
                    491:                        noiu.name = cp;
                    492:                        id1 = 0;
                    493:                }
                    494: 
                    495:                if (usernames) {
                    496:                        uid_t uid;
                    497:                        if (user_to_uid(colon+1, &uid, True))
                    498:                                add_to_list(idmap_ptr, id1, noiu, uid, flags);
                    499:                        else {
                    500:                                rprintf(FERROR,
                    501:                                    "Unknown --usermap name on receiver: %s\n",
                    502:                                    colon+1);
                    503:                        }
                    504:                } else {
                    505:                        gid_t gid;
                    506:                        if (group_to_gid(colon+1, &gid, True))
                    507:                                add_to_list(idmap_ptr, id1, noiu, gid, flags);
                    508:                        else {
                    509:                                rprintf(FERROR,
                    510:                                    "Unknown --groupmap name on receiver: %s\n",
                    511:                                    colon+1);
                    512:                        }
                    513:                }
                    514: 
                    515:                if (cp == map)
                    516:                        break;
                    517: 
                    518:                *--cp = '\0'; /* replace comma */
                    519:        }
                    520: 
                    521:        /* The 0 user/group doesn't get its name sent, so add it explicitly. */
                    522:        recv_add_id(idlist_ptr, *idmap_ptr, 0,
                    523:                    numeric_ids ? NULL : usernames ? uid_to_user(0) : gid_to_group(0));
                    524: }
                    525: 
                    526: #ifdef HAVE_GETGROUPLIST
1.1.1.3 ! misho     527: const char *getallgroups(uid_t uid, item_list *gid_list)
1.1.1.2   misho     528: {
                    529:        struct passwd *pw;
1.1.1.3 ! misho     530:        gid_t *gid_array;
        !           531:        int size;
        !           532: 
1.1.1.2   misho     533:        if ((pw = getpwuid(uid)) == NULL)
                    534:                return "getpwuid failed";
1.1.1.3 ! misho     535: 
        !           536:        gid_list->count = 0; /* We're overwriting any items in the list */
        !           537:        EXPAND_ITEM_LIST(gid_list, gid_t, 32);
        !           538:        size = gid_list->malloced;
        !           539: 
1.1.1.2   misho     540:        /* Get all the process's groups, with the pw_gid group first. */
1.1.1.3 ! misho     541:        if (getgrouplist(pw->pw_name, pw->pw_gid, gid_list->items, &size) < 0) {
        !           542:                if (size > (int)gid_list->malloced) {
        !           543:                        gid_list->count = gid_list->malloced;
        !           544:                        EXPAND_ITEM_LIST(gid_list, gid_t, size);
        !           545:                        if (getgrouplist(pw->pw_name, pw->pw_gid, gid_list->items, &size) < 0)
        !           546:                                size = -1;
        !           547:                } else
        !           548:                        size = -1;
        !           549:                if (size < 0)
        !           550:                        return "getgrouplist failed";
        !           551:        }
        !           552:        gid_list->count = size;
        !           553:        gid_array = gid_list->items;
        !           554: 
1.1.1.2   misho     555:        /* Paranoia: is the default group not first in the list? */
1.1.1.3 ! misho     556:        if (gid_array[0] != pw->pw_gid) {
1.1.1.2   misho     557:                int j;
1.1.1.3 ! misho     558:                for (j = 1; j < size; j++) {
        !           559:                        if (gid_array[j] == pw->pw_gid)
1.1.1.2   misho     560:                                break;
                    561:                }
1.1.1.3 ! misho     562:                if (j == size) { /* The default group wasn't found! */
        !           563:                        EXPAND_ITEM_LIST(gid_list, gid_t, size+1);
        !           564:                        gid_array = gid_list->items;
        !           565:                }
        !           566:                gid_array[j] = gid_array[0];
        !           567:                gid_array[0] = pw->pw_gid;
1.1.1.2   misho     568:        }
1.1.1.3 ! misho     569: 
1.1.1.2   misho     570:        return NULL;
                    571: }
                    572: #endif

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>