]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/idmapd/idmapd.c
more idmapd updates
[nfs-utils.git] / utils / idmapd / idmapd.c
1 /*
2  *  idmapd.c
3  *
4  *  Userland daemon for idmap.
5  *
6  *  Copyright (c) 2002 The Regents of the University of Michigan.
7  *  All rights reserved.
8  *
9  *  Marius Aamodt Eriksen <marius@umich.edu>
10  *
11  *  Redistribution and use in source and binary forms, with or without
12  *  modification, are permitted provided that the following conditions
13  *  are met:
14  *
15  *  1. Redistributions of source code must retain the above copyright
16  *     notice, this list of conditions and the following disclaimer.
17  *  2. Redistributions in binary form must reproduce the above copyright
18  *     notice, this list of conditions and the following disclaimer in the
19  *     documentation and/or other materials provided with the distribution.
20  *  3. Neither the name of the University nor the names of its
21  *     contributors may be used to endorse or promote products derived
22  *     from this software without specific prior written permission.
23  *
24  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
25  *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
26  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27  *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
31  *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32  *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33  *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34  *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35  */
36
37 #include <sys/types.h>
38 #include <sys/time.h>
39 #include <sys/poll.h>
40 #include <sys/socket.h>
41 #include <sys/stat.h>
42 #include <time.h>
43
44 #include "nfs_idmap.h"
45
46 #include <err.h>
47 #include <errno.h>
48 #include <event.h>
49 #include <fcntl.h>
50 #include <dirent.h>
51 #include <unistd.h>
52 #include <netdb.h>
53 #include <signal.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <pwd.h>
58 #include <grp.h>
59 #include <limits.h>
60 #include <ctype.h>
61 #include <nfsidmap.h>
62
63 #ifdef HAVE_CONFIG_H
64 #include "config.h"
65 #endif /* HAVE_CONFIG_H */
66
67 #include "cfg.h"
68 #include "queue.h"
69 #include "nfslib.h"
70
71 #ifndef PIPEFS_DIR
72 #define PIPEFS_DIR  "/var/lib/nfs/rpc_pipefs/"
73 #endif
74
75 #ifndef NFSD_DIR
76 #define NFSD_DIR  "/proc/net/rpc"
77 #endif
78
79 #ifndef NFS4NOBODY_USER
80 #define NFS4NOBODY_USER "nobody"
81 #endif
82
83 #ifndef NFS4NOBODY_GROUP
84 #define NFS4NOBODY_GROUP "nobody"
85 #endif
86
87 /* From Niels */
88 #define CONF_SAVE(w, f) do {                    \
89         char *p = f;                            \
90         if (p != NULL)                          \
91                 (w) = p;                        \
92 } while (0)
93
94 #define IC_IDNAME 1
95 #define IC_NAMEID 2
96 struct idmap_client {
97         int                        ic_fd;
98         int                        ic_dirfd;
99         char                       ic_clid[30];
100         char                       ic_path[PATH_MAX];
101         int                        ic_scanned;
102         struct event               ic_event;
103         char                      *ic_id;
104         short                      ic_which;
105         TAILQ_ENTRY(idmap_client)  ic_next;
106 };
107
108 TAILQ_HEAD(idmap_clientq, idmap_client);
109
110 static void dirscancb(int, short, void *);
111 static void clntscancb(int, short, void *);
112 static void svrreopen(int, short, void *);
113 static int  nfsopen(struct idmap_client *);
114 static void nfscb(int, short, void *);
115 static void nfsdcb(int, short, void *);
116 static int  validateascii(char *, u_int32_t);
117 static int  addfield(char **, ssize_t *, char *);
118 static int  getfield(char **, char *, size_t);
119
120 static void imconv(struct idmap_client *, struct idmap_msg *);
121 static void idtonameres(struct idmap_msg *);
122 static void nametoidres(struct idmap_msg *);
123
124 static int nfsdopen(char *);
125 static int nfsdopenone(struct idmap_client *, short, char *);
126 static void nfsdreopen(void);
127
128 size_t  strlcat(char *, const char *, size_t);
129 size_t  strlcpy(char *, const char *, size_t);
130 ssize_t atomicio(ssize_t (*)(), int, void *, size_t);
131 int     daemon(int, int);
132
133 static int verbose = 0;
134 static char domain[512];
135 static char pipefsdir[PATH_MAX];
136 static char *nobodyuser, *nobodygroup;
137 static uid_t nobodyuid;
138 static gid_t nobodygid;
139 static struct idmap_client nfsd_ic[2];
140
141 /* Used by cfg.c */
142 char *conf_path;
143
144 int
145 main(int argc, char **argv)
146 {
147         int fd = 0, opt, fg = 0, nfsdret = -1;
148         struct idmap_clientq icq;
149         struct event rootdirev, clntdirev, svrdirev;
150         struct event initialize;
151         struct passwd *pw;
152         struct group *gr;
153         struct stat sb;
154         char *xpipefsdir = NULL;
155         char *xdomain = NULL;
156         int serverstart = 1, clientstart = 1;
157
158         conf_path = _PATH_IDMAPDCONF;
159         nobodyuser = NFS4NOBODY_USER;
160         nobodygroup = NFS4NOBODY_GROUP;
161         strlcpy(pipefsdir, PIPEFS_DIR, sizeof(pipefsdir));
162
163 #define GETOPTSTR "vfd:p:U:G:c:CS"
164         opterr=0; /* Turn off error messages */
165         while ((opt = getopt(argc, argv, GETOPTSTR)) != -1) {
166                 if (opt == 'c')
167                         conf_path = optarg;
168                 if (opt == '?') {
169                         if (strchr(GETOPTSTR, optopt))
170                                 errx(1, "'-%c' option requires an argument.", optopt);
171                         else
172                                 errx(1, "'-%c' is an invalid argument.", optopt);
173                 }
174         }
175         optind = 1;
176
177         if (stat(conf_path, &sb) == -1 && (errno == ENOENT || errno == EACCES)) {
178                 warn("Skipping configuration file \"%s\"", conf_path);
179         } else {
180                 conf_init();
181                 verbose = conf_get_num("General", "Verbosity", 0);
182                 CONF_SAVE(xpipefsdir, conf_get_str("General", "Pipefs-Directory"));
183                 CONF_SAVE(xdomain, conf_get_str("General", "Domain"));
184                 if (xpipefsdir != NULL)
185                         strlcpy(pipefsdir, xpipefsdir, sizeof(pipefsdir));
186                 if (xdomain != NULL)
187                         strlcpy(domain, xdomain, sizeof(domain));
188                 CONF_SAVE(nobodyuser, conf_get_str("Mapping", "Nobody-User"));
189                 CONF_SAVE(nobodygroup, conf_get_str("Mapping", "Nobody-Group"));
190         }
191
192         while ((opt = getopt(argc, argv, GETOPTSTR)) != -1)
193                 switch (opt) {
194                 case 'v':
195                         verbose++;
196                         break;
197                 case 'f':
198                         fg = 1;
199                         break;
200                 case 'p':
201                         strlcpy(pipefsdir, optarg, sizeof(pipefsdir));
202                         break;
203                 case 'd':
204                 case 'U':
205                 case 'G':
206                         errx(1, "the -d, -U, and -G options have been removed;"
207                                 " please use the configuration file instead.");
208                 case 'C':
209                         serverstart = 0;
210                         break;
211                 case 'S':
212                         clientstart = 0;
213                         break;
214                 default:
215                         break;
216                 }
217
218         if (!serverstart && !clientstart)
219                 errx(1, "it is illegal to specify both -C and -S");
220
221         strncat(pipefsdir, "/nfs", sizeof(pipefsdir));
222
223         if (domain[0] == '\0') {
224                 struct hostent *he;
225                 char hname[64], *c;
226
227                 if (gethostname(hname, sizeof(hname)) == -1)
228                         errx(1, "Error getting hostname");
229
230                 if ((he = gethostbyname(hname)) == NULL)
231                         errx(1, "Error resolving hostname: %s", hname);
232
233                 if ((c = strchr(he->h_name, '.')) == NULL || *++c == '\0')
234                         errx(1, "Error resolving domain, "
235                             "please use the -d switch");
236
237                 strlcpy(domain, c, sizeof(domain));
238         }
239
240         if ((pw = getpwnam(nobodyuser)) == NULL)
241                 errx(1, "Could not find user \"%s\"", nobodyuser);
242         nobodyuid = pw->pw_uid;
243
244         if ((gr = getgrnam(nobodygroup)) == NULL)
245                 errx(1, "Could not find group \"%s\"", nobodygroup);
246         nobodygid = gr->gr_gid;
247
248         if (strlen(domain) == 0)
249                 errx(1, "Invalid domain; please specify with -d switch");
250
251         if (verbose > 2)
252                 warnx("Using domain \"%s\"", domain);
253
254         if (!fg)
255                 daemon(0, 0);
256
257         event_init();
258
259         if (serverstart)
260                 nfsdret = nfsdopen(NFSD_DIR);
261
262         if (clientstart) {
263                 struct timeval now = {
264                         .tv_sec = 0,
265                         .tv_usec = 0,
266                 };
267
268                 if ((fd = open(pipefsdir, O_RDONLY)) == -1)
269                         err(1, "open(%s)", pipefsdir);
270
271                 if (fcntl(fd, F_SETSIG, SIGUSR1) == -1)
272                         err(1, "fcntl(%s)", pipefsdir);
273                 if (fcntl(fd, F_NOTIFY,
274                         DN_CREATE | DN_DELETE | DN_MODIFY | DN_MULTISHOT) == -1)
275                         err(1, "fcntl(%s)", pipefsdir);
276
277                 TAILQ_INIT(&icq);
278
279                 /* These events are persistent */
280                 signal_set(&rootdirev, SIGUSR1, dirscancb, &icq);
281                 signal_add(&rootdirev, NULL);
282                 signal_set(&clntdirev, SIGUSR2, clntscancb, &icq);
283                 signal_add(&clntdirev, NULL);
284                 signal_set(&svrdirev, SIGHUP, svrreopen, NULL);
285                 signal_add(&svrdirev, NULL);
286
287                 /* Fetch current state */
288                 /* (Delay till start of event_dispatch to avoid possibly losing
289                  * a SIGUSR1 between here and the call to event_dispatch().) */
290                 evtimer_set(&initialize, dirscancb, &icq);
291                 evtimer_add(&initialize, &now);
292         }
293
294         if (nfsdret != 0 && fd == 0)
295                 errx(1, "Neither NFS client nor NFSd found");
296
297         if (event_dispatch() < 0)
298                 errx(1, "event_dispatch: returns errno %d (%s)", errno, strerror(errno));
299         /* NOTREACHED */
300         return 1;
301 }
302
303 static void
304 dirscancb(int fd, short which, void *data)
305 {
306         int nent, i;
307         struct dirent **ents;
308         struct idmap_client *ic;
309         char path[PATH_MAX];
310         struct idmap_clientq *icq = data;
311
312         nent = scandir(pipefsdir, &ents, NULL, alphasort);
313         if (nent == -1) {
314                 warn("scandir(%s)", pipefsdir);
315                 return;
316         }
317
318         for (i = 0;  i < nent; i++) {
319                 if (ents[i]->d_reclen > 4 &&
320                     strncmp(ents[i]->d_name, "clnt", 4) == 0) {
321                         TAILQ_FOREACH(ic, icq, ic_next)
322                             if (strcmp(ents[i]->d_name + 4, ic->ic_clid) == 0)
323                                     break;
324                         if (ic != NULL)
325                                 goto next;
326
327                         if ((ic = calloc(1, sizeof(*ic))) == NULL)
328                                 return;
329                         strlcpy(ic->ic_clid, ents[i]->d_name + 4,
330                             sizeof(ic->ic_clid));
331                         path[0] = '\0';
332                         snprintf(path, sizeof(path), "%s/%s",
333                             pipefsdir, ents[i]->d_name);
334
335                         if ((ic->ic_dirfd = open(path, O_RDONLY, 0)) == -1) {
336                                 warn("open(%s)", path);
337                                 free(ic);
338                                 return;
339                         }
340
341                         strlcat(path, "/idmap", sizeof(path));
342                         strlcpy(ic->ic_path, path, sizeof(ic->ic_path));
343
344                         if (verbose > 0)
345                                 warnx("New client: %s", ic->ic_clid);
346
347                         if (nfsopen(ic) == -1) {
348                                 close(ic->ic_dirfd);
349                                 free(ic);
350                                 return;
351                         }
352
353                         ic->ic_id = "Client";
354
355                         TAILQ_INSERT_TAIL(icq, ic, ic_next);
356
357                 next:
358                         ic->ic_scanned = 1;
359                 }
360         }
361
362         TAILQ_FOREACH(ic, icq, ic_next) {
363                 if (!ic->ic_scanned) {
364                         event_del(&ic->ic_event);
365                         close(ic->ic_fd);
366                         close(ic->ic_dirfd);
367                         TAILQ_REMOVE(icq, ic, ic_next);
368                         if (verbose > 0) {
369                                 warnx("Stale client: %s", ic->ic_clid);
370                                 warnx("\t-> closed %s", ic->ic_path);
371                         }
372                         free(ic);
373                 } else
374                         ic->ic_scanned = 0;
375         }
376         return;
377 }
378
379 static void
380 svrreopen(int fd, short which, void *data)
381 {
382         nfsdreopen();
383 }
384
385 static void
386 clntscancb(int fd, short which, void *data)
387 {
388         struct idmap_clientq *icq = data;
389         struct idmap_client *ic;
390
391         TAILQ_FOREACH(ic, icq, ic_next)
392                 if (ic->ic_fd == -1 && nfsopen(ic) == -1) {
393                         close(ic->ic_dirfd);
394                         TAILQ_REMOVE(icq, ic, ic_next);
395                         free(ic);
396                 }
397 }
398
399 static void
400 nfsdcb(int fd, short which, void *data)
401 {
402         struct idmap_client *ic = data;
403         struct idmap_msg im;
404         u_char buf[IDMAP_MAXMSGSZ + 1];
405         size_t len, bsiz;
406         char *bp, typebuf[IDMAP_MAXMSGSZ],
407                 buf1[IDMAP_MAXMSGSZ], authbuf[IDMAP_MAXMSGSZ], *p;
408
409         if (which != EV_READ)
410                 goto out;
411
412         if ((len = read(ic->ic_fd, buf, sizeof(buf))) == -1) {
413                 warnx("nfsdcb: read(%s) failed: errno %d (%s)",
414                         ic->ic_path, errno, strerror(errno));
415                 goto out;
416         }
417
418         /* Get rid of newline and terminate buffer*/
419         buf[len - 1] = '\0';
420         bp = buf;
421
422         memset(&im, 0, sizeof(im));
423
424         /* Authentication name -- ignored for now*/
425         if (getfield(&bp, authbuf, sizeof(authbuf)) == -1) {
426                 warnx("nfsdcb: bad authentication name in upcall\n");
427                 return;
428         }
429         if (getfield(&bp, typebuf, sizeof(typebuf)) == -1) {
430                 warnx("nfsdcb: bad type in upcall\n");
431                 return;
432         }
433         if (verbose > 0)
434                 warnx("nfsdcb: authbuf=%s authtype=%s", authbuf, typebuf);
435
436         im.im_type = strcmp(typebuf, "user") == 0 ?
437                 IDMAP_TYPE_USER : IDMAP_TYPE_GROUP;
438
439         switch (ic->ic_which) {
440         case IC_NAMEID:
441                 im.im_conv = IDMAP_CONV_NAMETOID;
442                 if (getfield(&bp, im.im_name, sizeof(im.im_name)) == -1) {
443                         warnx("nfsdcb: bad name in upcall\n");
444                         return;
445                 }
446                 break;
447         case IC_IDNAME:
448                 im.im_conv = IDMAP_CONV_IDTONAME;
449                 if (getfield(&bp, buf1, sizeof(buf1)) == -1) {
450                         warnx("nfsdcb: bad id in upcall\n");
451                         return;
452                 }
453                 if ((im.im_id = strtoul(buf1, (char **)NULL, 10)) == ULONG_MAX &&
454                     errno == ERANGE) {
455                         warnx("nfsdcb: id '%s' too big!\n", buf1);
456                         return;
457                 }
458
459                 break;
460         default:
461                 warnx("Unknown which type %d", ic->ic_which);
462                 return;
463         }
464
465         imconv(ic, &im);
466
467         buf[0] = '\0';
468         bp = buf;
469         bsiz = sizeof(buf);
470
471         /* Authentication name */
472         addfield(&bp, &bsiz, authbuf);
473
474         switch (ic->ic_which) {
475         case IC_NAMEID:
476                 /* Type */
477                 p = im.im_type == IDMAP_TYPE_USER ? "user" : "group";
478                 addfield(&bp, &bsiz, p);
479                 /* Name */
480                 addfield(&bp, &bsiz, im.im_name);
481 #define NFSD_EXPIRY 300 /* seconds */
482                 /* expiry */
483                 snprintf(buf1, sizeof(buf1), "%lu", time(NULL) + NFSD_EXPIRY);
484                 addfield(&bp, &bsiz, buf1);
485                 /* ID */
486                 snprintf(buf1, sizeof(buf1), "%u", im.im_id);
487                 addfield(&bp, &bsiz, buf1);
488
489                 //if (bsiz == sizeof(buf)) /* XXX */
490
491                 bp[-1] = '\n';
492
493                 break;
494         case IC_IDNAME:
495                 /* Type */
496                 p = im.im_type == IDMAP_TYPE_USER ? "user" : "group";
497                 addfield(&bp, &bsiz, p);
498                 /* ID */
499                 snprintf(buf1, sizeof(buf1), "%u", im.im_id);
500                 addfield(&bp, &bsiz, buf1);
501                 /* expiry */
502                 snprintf(buf1, sizeof(buf1), "%lu", time(NULL) + NFSD_EXPIRY);
503                 addfield(&bp, &bsiz, buf1);
504                 /* Name */
505                 addfield(&bp, &bsiz, im.im_name);
506
507                 bp[-1] = '\n';
508
509                 break;
510         default:
511                 warnx("Unknown which type %d", ic->ic_which);
512                 return;
513         }
514
515         bsiz = sizeof(buf) - bsiz;
516
517         if (atomicio(write, ic->ic_fd, buf, bsiz) != bsiz)
518                 warnx("nfsdcb: write(%s) failed: errno %d (%s)",
519                         ic->ic_path, errno, strerror(errno));
520
521 out:
522         event_add(&ic->ic_event, NULL);
523 }
524
525 static void
526 imconv(struct idmap_client *ic, struct idmap_msg *im)
527 {
528         switch (im->im_conv) {
529         case IDMAP_CONV_IDTONAME:
530                 idtonameres(im);
531                 if (verbose > 1)
532                         warnx("%s %s: (%s) id \"%d\" -> name \"%s\"",
533                             ic->ic_id, ic->ic_clid,
534                             im->im_type == IDMAP_TYPE_USER ? "user" : "group",
535                             im->im_id, im->im_name);
536                 break;
537         case IDMAP_CONV_NAMETOID:
538                 if (validateascii(im->im_name, sizeof(im->im_name)) == -1) {
539                         im->im_status |= IDMAP_STATUS_INVALIDMSG;
540                         return;
541                 }
542                 nametoidres(im);
543                 if (verbose > 1)
544                         warnx("%s %s: (%s) name \"%s\" -> id \"%d\"",
545                             ic->ic_id, ic->ic_clid,
546                             im->im_type == IDMAP_TYPE_USER ? "user" : "group",
547                             im->im_name, im->im_id);
548                 break;
549         default:
550                 warnx("Invalid conversion type (%d) in message", im->im_conv);
551                 im->im_status |= IDMAP_STATUS_INVALIDMSG;
552                 break;
553         }
554 }
555
556 static void
557 nfscb(int fd, short which, void *data)
558 {
559         struct idmap_client *ic = data;
560         struct idmap_msg im;
561
562         if (which != EV_READ)
563                 goto out;
564
565         if (atomicio(read, ic->ic_fd, &im, sizeof(im)) != sizeof(im)) {
566                 if (verbose > 0)
567                         warn("read(%s)", ic->ic_path);
568                 if (errno == EPIPE)
569                         return;
570                 goto out;
571         }
572
573         imconv(ic, &im);
574
575         if (atomicio(write, ic->ic_fd, &im, sizeof(im)) != sizeof(im))
576                 warn("write(%s)", ic->ic_path);
577 out:
578         event_add(&ic->ic_event, NULL);
579 }
580
581 static void
582 nfsdreopen_one(struct idmap_client *ic)
583 {
584         int fd;
585
586         if (verbose > 0)
587                 warnx("ReOpening %s", ic->ic_path);
588         if ((fd = open(ic->ic_path, O_RDWR, 0)) != -1) {
589                 if (ic->ic_fd != -1)
590                         close(ic->ic_fd);
591                 ic->ic_event.ev_fd = ic->ic_fd = fd;
592                 if ((ic->ic_event.ev_flags & EVLIST_INIT) == 0) {
593                         event_set(&ic->ic_event, ic->ic_fd, EV_READ, nfsdcb, ic);
594                         event_add(&ic->ic_event, NULL);
595                 }
596         } else {
597                 warnx("nfsdreopen: Opening '%s' failed: errno %d (%s)",
598                         ic->ic_path, errno, strerror(errno));
599         }
600 }
601
602 /*
603  * Note: nfsdreopen assumes nfsdopen has already been called
604  */
605 static void
606 nfsdreopen()
607 {
608         nfsdreopen_one(&nfsd_ic[IC_NAMEID]);
609         nfsdreopen_one(&nfsd_ic[IC_IDNAME]);
610         return;
611 }
612
613 static int
614 nfsdopen(char *path)
615 {
616         return ((nfsdopenone(&nfsd_ic[0], IC_NAMEID, path) == 0 &&
617                     nfsdopenone(&nfsd_ic[1], IC_IDNAME, path) == 0) ? 0 : -1);
618 }
619
620 static int
621 nfsdopenone(struct idmap_client *ic, short which, char *path)
622 {
623         char *whichstr;
624
625         whichstr = which == IC_IDNAME ? "idtoname" : "nametoid";
626         snprintf(ic->ic_path, sizeof(ic->ic_path),
627                 "%s/nfs4.%s/channel", path, whichstr);
628         if ((ic->ic_fd = open(ic->ic_path, O_RDWR, 0)) == -1) {
629                 if (verbose > 0)
630                         warnx("Opening %s failed: errno %d (%s)",
631                                 ic->ic_path, errno, strerror(errno));
632                 return (-1);
633         }
634
635         event_set(&ic->ic_event, ic->ic_fd, EV_READ, nfsdcb, ic);
636         event_add(&ic->ic_event, NULL);
637
638         ic->ic_which = which;
639         ic->ic_id = "Server";
640         strlcpy(ic->ic_clid, domain, sizeof(ic->ic_clid));
641
642         if (verbose > 0)
643                 warnx("Opened %s", ic->ic_path);
644
645         return (0);
646 }
647
648 static int
649 nfsopen(struct idmap_client *ic)
650 {
651         if ((ic->ic_fd = open(ic->ic_path, O_RDWR, 0)) == -1) {
652                 switch (errno) {
653                 case ENOENT:
654                         fcntl(ic->ic_dirfd, F_SETSIG, SIGUSR2);
655                         fcntl(ic->ic_dirfd, F_NOTIFY,
656                             DN_CREATE | DN_DELETE | DN_MULTISHOT);
657                         break;
658                 default:
659                         warn("open(%s)", ic->ic_path);
660                         return (-1);
661                 }
662         } else {
663                 event_set(&ic->ic_event, ic->ic_fd, EV_READ, nfscb, ic);
664                 event_add(&ic->ic_event, NULL);
665                 fcntl(ic->ic_dirfd, F_SETSIG, 0);
666                 fcntl(ic->ic_dirfd, F_NOTIFY, 0);
667                 if (verbose > 0)
668                         warnx("Opened %s", ic->ic_path);
669         }
670
671         return (0);
672 }
673
674 static int write_name(char *dest, char *localname, char *domain, size_t len)
675 {
676         if (strlen(localname) + 1 + strlen(domain) + 1 > len) {
677                 return -ENOMEM; /* XXX: Is there an -ETOOLONG? */
678         }
679         strcpy(dest, localname);
680         strcat(dest, "@");
681         strcat(dest, domain);
682         return 0;
683 }
684
685 static void
686 idtonameres(struct idmap_msg *im)
687 {
688         int ret = 0;
689
690         switch (im->im_type) {
691         case IDMAP_TYPE_USER:
692                 ret = nfs4_uid_to_name(im->im_id, domain, im->im_name,
693                                 sizeof(im->im_name));
694                 if (ret)
695                         write_name(im->im_name, nobodyuser, domain,
696                                         sizeof(im->im_name));
697                 break;
698         case IDMAP_TYPE_GROUP:
699                 ret = nfs4_gid_to_name(im->im_id, domain, im->im_name,
700                                 sizeof(im->im_name));
701                 if (ret)
702                         write_name(im->im_name, nobodygroup, domain,
703                                         sizeof(im->im_name));
704                 break;
705         }
706         /* XXX Hack? would rather return failure instead of writing nobody
707          * as above, but kernel seems not to deal well with that as of
708          * 2.6.8-rc3. */
709         im->im_status = IDMAP_STATUS_SUCCESS;
710 }
711
712 static void
713 nametoidres(struct idmap_msg *im)
714 {
715         int ret = 0;
716
717         switch (im->im_type) {
718         case IDMAP_TYPE_USER:
719                 ret = nfs4_name_to_uid(im->im_name, &im->im_id);
720                 if (ret)
721                         im->im_id = nobodyuid;
722                 break;
723         case IDMAP_TYPE_GROUP:
724                 ret = nfs4_name_to_gid(im->im_name, &im->im_id);
725                 if (ret)
726                         im->im_id = nobodygid;
727                 break;
728         }
729         /* XXX Hack? would rather return failure instead of writing nobody
730          * as above, but kernel seems not to deal well with that as of
731          * 2.6.8-rc3. */
732         im->im_status = IDMAP_STATUS_SUCCESS;
733 }
734
735 static int
736 validateascii(char *string, u_int32_t len)
737 {
738         int i;
739
740         for (i = 0; i < len; i++) {
741                 if (string[i] == '\0')
742                         break;
743
744                 if (string[i] & 0x80)
745                         return (-1);
746         }
747
748         if (string[i] != '\0')
749                 return (-1);
750
751         return (i + 1);
752 }
753
754 static int
755 addfield(char **bpp, ssize_t *bsizp, char *fld)
756 {
757         char ch, *bp = *bpp;
758         ssize_t bsiz = *bsizp;
759
760         while ((ch = *fld++) != '\0' && bsiz > 0) {
761                 switch(ch) {
762                 case ' ':
763                 case '\t':
764                 case '\n':
765                 case '\\':
766                         if (bsiz >= 4) {
767                                 bp += snprintf(bp, bsiz, "\\%03o", ch);
768                                 bsiz -= 4;
769                         }
770                         break;
771                 default:
772                         *bp++ = ch;
773                         bsiz--;
774                         break;
775                 }
776         }
777
778         if (bsiz < 1 || ch != '\0')
779                 return (-1);
780
781         *bp++ = ' ';
782         bsiz--;
783
784         *bpp = bp;
785         *bsizp = bsiz;
786
787         return (0);
788 }
789
790 static int
791 getfield(char **bpp, char *fld, size_t fldsz)
792 {
793         char *bp;
794         u_int val, n;
795
796         while ((bp = strsep(bpp, " ")) != NULL && bp[0] == '\0')
797                 ;
798
799         if (bp == NULL || bp[0] == '\0' || bp[0] == '\n')
800                 return (-1);
801
802         while (*bp != '\0' && fldsz > 1) {
803                 if (*bp == '\\') {
804                         if ((n = sscanf(bp, "\\%03o", &val)) != 1)
805                                 return (-1);
806                         if (val > (char)-1)
807                                 return (-1);
808                         *fld++ = (char)val;
809                         bp += 4;
810                 } else {
811                         *fld++ = *bp;
812                         bp++;
813                 }
814                 fldsz--;
815         }
816
817         if (*bp != '\0')
818                 return (-1);
819         *fld = '\0';
820
821         return (0);
822 }