Whamcloud - gitweb
3441d81bb569ddb76c75fa9b8217ce19bbb8becd
[fs/lustre-release.git] / libsysio / src / init.c
1 /*
2  *    This Cplant(TM) source code is the property of Sandia National
3  *    Laboratories.
4  *
5  *    This Cplant(TM) source code is copyrighted by Sandia National
6  *    Laboratories.
7  *
8  *    The redistribution of this Cplant(TM) source code is subject to the
9  *    terms of the GNU Lesser General Public License
10  *    (see cit/LGPL or http://www.gnu.org/licenses/lgpl.html)
11  *
12  *    Cplant(TM) Copyright 1998-2005 Sandia Corporation. 
13  *    Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive
14  *    license for use of this work by or on behalf of the US Government.
15  *    Export of this program may require a license from the United States
16  *    Government.
17  */
18
19 /*
20  * This library is free software; you can redistribute it and/or
21  * modify it under the terms of the GNU Lesser General Public
22  * License as published by the Free Software Foundation; either
23  * version 2.1 of the License, or (at your option) any later version.
24  * 
25  * This library is distributed in the hope that it will be useful,
26  * but WITHOUT ANY WARRANTY; without even the implied warranty of
27  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28  * Lesser General Public License for more details.
29  * 
30  * You should have received a copy of the GNU Lesser General Public
31  * License along with this library; if not, write to the Free Software
32  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33  *
34  * Questions or comments about this library should be sent to:
35  *
36  * Lee Ward
37  * Sandia National Laboratories, New Mexico
38  * P.O. Box 5800
39  * Albuquerque, NM 87185-1110
40  *
41  * lee@sandia.gov
42  */
43
44 #define _BSD_SOURCE
45
46 #if SYSIO_TRACING
47 #include <stdio.h>
48 #endif
49 #include <stdlib.h>
50 #if SYSIO_TRACING
51 #include <sys/syscall.h>
52 #endif
53 #include <unistd.h>
54 #include <string.h>
55 #include <errno.h>
56 #if SYSIO_TRACING
57 #include <stdarg.h>
58 #endif
59 #include <limits.h>
60 #include <assert.h>
61 #include <sys/types.h>
62 #include <sys/stat.h>
63 #include <fcntl.h>
64 #include <sys/uio.h>
65 #include <sys/queue.h>
66
67 #include "sysio.h"
68 #include "xtio.h"
69 #if SYSIO_TRACING
70 #include "native.h"
71 #endif
72 #include "inode.h"
73 #include "fs.h"
74 #include "mount.h"
75 #include "file.h"
76 #include "dev.h"
77
78 #ifdef STDFD_DEV
79 #include "stdfd.h"
80 #endif
81
82 #if SYSIO_TRACING
83
84 /*
85  * Tracing callback record.
86  */
87 struct trace_callback {
88         TAILQ_ENTRY(trace_callback) links;
89         void    (*f)(const char *file, const char *func, int line);
90 };
91
92 /*
93  * Initialize a tracing callback record.
94  */
95 #define TCB_INIT(__tcb, __f) \
96         do { \
97                 (__tcb)->f = (__f); \
98         } while (0);
99
100 /*
101  * Trace queue head record.
102  */
103 TAILQ_HEAD(trace_q, trace_callback);
104
105 /*
106  * The entry and exit queue heads, and queue pointers.
107  */
108 static struct trace_q _sysio_entry_trace_head;
109 void    *_sysio_entry_trace_q = &_sysio_entry_trace_head;
110 static struct trace_q _sysio_exit_trace_head;
111 void    *_sysio_exit_trace_q = &_sysio_exit_trace_head;
112 #endif
113
114 /*
115  * White space characters.
116  */
117 #define IGNORE_WHITE            " \t\r\n"
118
119 /*
120  * Check if long overflows integer range.
121  */
122 #if LONG_MAX <= INT_MAX
123 #define _irecheck(_l, _e) \
124         ((_l) == LONG_MAX && (_e) == ERANGE)
125 #else
126 #define _irecheck(_l, _e) \
127         ((_l) > INT_MAX)
128 #endif
129
130 /*
131  * In sysio_init we'll allow simple comments, strings outside {}
132  * delimited by COMMENT_INTRO, and '\n' or '\0'
133  */
134 #define COMMENT_INTRO           '#'
135
136 /*
137  * Sysio library initialization. Must be called before anything else in the
138  * library.
139  */
140 int
141 _sysio_init()
142 {
143         int     err;
144 #ifdef WITH_SOCKETS
145         extern int _sysio_sockets_init(void);
146 #endif
147
148 #if SYSIO_TRACING
149         /*
150          * Initialize tracing callback queues.
151          */
152         TAILQ_INIT(&_sysio_entry_trace_head);
153         TAILQ_INIT(&_sysio_exit_trace_head);
154 #endif
155
156         err = _sysio_ioctx_init();
157         if (err)
158                 goto error;
159         err = _sysio_i_init();
160         if (err)
161                 goto error;
162         err = _sysio_mount_init();
163         if (err)
164                 goto error;
165
166         err = _sysio_dev_init();
167         if (err)
168                 goto error;
169 #ifdef STDFD_DEV
170         err = _sysio_stdfd_init();
171         if (err)
172                 goto error;
173 #endif
174 #ifdef WITH_SOCKETS
175         err = _sysio_sockets_init();
176         if (err)
177                 goto error;
178 #endif
179
180         goto out;
181 error:
182         errno = -err;
183 out:
184         /*
185          * Unlike all other _sysio routines, this one returns with errno
186          * set. It also returns the error, as usual.
187          */
188         return err;
189 }
190
191 /*
192  * Sysio library shutdown.
193  */
194 void
195 _sysio_shutdown()
196 {
197
198         if (!(_sysio_fd_close_all() == 0 &&
199               _sysio_unmount_all() == 0))
200                         abort();
201
202 #if ZERO_SUM_MEMORY
203         _sysio_fd_shutdown();
204         _sysio_i_shutdown();
205         _sysio_fssw_shutdown();
206 #if SYSIO_TRACING
207         {
208                 struct trace_callback *tcb;
209
210                 /*
211                  * Empty the trace queues and free the entries.
212                  */
213                 while ((tcb = _sysio_entry_trace_head.tqh_first) != NULL) {
214                         TAILQ_REMOVE(&_sysio_entry_trace_head, tcb, links);
215                         free(tcb);
216                 }
217                 while ((tcb = _sysio_exit_trace_head.tqh_first) != NULL) {
218                         TAILQ_REMOVE(&_sysio_exit_trace_head, tcb, links);
219                         free(tcb);
220                 }
221         }
222 #endif
223 #endif
224 }
225
226 #if SYSIO_TRACING
227
228 #if !(defined(_HAVE_ASPRINTF) && _HAVE_ASPRINTF)
229 /*
230  * Print a string to allocated memory.
231  */
232 static int
233 vasprintf(char **strp, const char *fmt, va_list ap)
234 {
235         size_t  siz;
236         int     oerrno;
237         char    *s;
238         va_list aq;
239         int     n;
240
241         siz = 50;
242         oerrno = errno;
243         if (!(s = malloc(siz))) {
244                 errno = oerrno;
245                 return -1;
246         }
247         for (;;) {
248                 va_copy(aq, ap);
249                 n = vsnprintf (s, siz, fmt, aq);
250                 va_end(aq);
251                 if (n > -1 && (size_t )n < siz)
252                         break;
253                 if (n > -1)                             /* glibc 2.1 */
254                         siz = n+1;                      /* precise */
255                 else                                    /* glibc 2.0 */
256                         siz *= 2;                       /* twice the old */
257                 if (!(s = realloc (s, siz)))
258                         break;
259         }
260         *strp = s;
261         errno = oerrno;
262         return n;
263 }
264
265 #if 0
266 static int
267 asprintf(char **strp, const char *fmt, ...)
268 {
269         va_list ap;
270         int     n;
271
272         va_start(ap, fmt);
273         n = vasprintf(strp, fmt, ap);
274         va_end(ap);
275         return n;
276 }
277 #endif
278 #endif /* !(defined(_HAVE_ASPRINTF) && _HAVE_ASPRINTF) */
279
280 static void
281 _sysio_cwrite(const char *buf, size_t len)
282 {
283         int     oerrno;
284
285         oerrno = errno;
286         (void )syscall(SYSIO_SYS_write, STDERR_FILENO, buf, len);
287         errno = oerrno;
288 }
289
290 /*
291  * Console printf.
292  */
293 void
294 _sysio_cprintf(const char *fmt, ...)
295 {
296         va_list ap;
297         int     len;
298         char    *buf;
299
300         va_start(ap, fmt);
301         buf = NULL;
302         len = vasprintf(&buf, fmt, ap);
303         va_end(ap);
304         if (len < 0)
305                 return;
306         _sysio_cwrite(buf, len);
307         free(buf);
308 }
309
310 /*
311  * Register a trace callback.
312  *
313  * The pointer to the trace record is returned.
314  */
315 void *
316 _sysio_register_trace(void *q,
317                       void (*f)(const char *file,
318                                 const char *func,
319                                 int line))
320 {
321         struct trace_callback *tcb;
322
323         tcb = malloc(sizeof(struct trace_callback));
324         if (!tcb)
325                 return NULL;
326         TCB_INIT(tcb, f);
327         TAILQ_INSERT_TAIL((struct trace_q *)q, tcb, links);
328         return tcb;
329 }
330
331 /*
332  * Remove a registered trace callback.
333  */
334 void
335 _sysio_remove_trace(void *q, void *p)
336 {
337
338         TAILQ_REMOVE((struct trace_q *)q, (struct trace_callback *)p, links);
339         free(p);
340 }
341
342 void
343 /*
344  * Run a trace queue, making all the callbacks.
345  */
346 _sysio_run_trace_q(void *q,
347                    const char *file,
348                    const char *func,
349                    int line)
350 {
351         struct trace_callback *tcb;
352
353         tcb = ((struct trace_q *)q)->tqh_first;
354         while (tcb) {
355                 (*tcb->f)(file, func, line);
356                 tcb = tcb->links.tqe_next;
357         }
358 }
359
360 static void
361 _sysio_trace_entry(const char *file __IS_UNUSED,
362                    const char *func,
363                    int line __IS_UNUSED)
364 {
365
366         _sysio_cprintf("+ENTER+ %s\n", func);
367 }
368
369 static void
370 _sysio_trace_exit(const char *file __IS_UNUSED,
371                   const char *func,
372                   int line __IS_UNUSED)
373 {
374
375         _sysio_cprintf("+EXIT+ %s\n", func);
376 }
377 #endif /* defined(SYSIO_TRACING) */
378
379 /* 
380  * (kind of)Duplicates strtok function.
381  *
382  * Given a buffer, returns the longest string
383  * that does not contain any delim characters.  Will
384  * remove ws and any characters in the ignore string.  
385  * Returns the token.  
386  *
387  * The parameter controlling acceptance controls whether a positive
388  * match for some delimiter be made or not. If set, then either a delimiter
389  * or NUL character is success.
390  *
391  */
392 const char *
393 _sysio_get_token(const char *buf,
394           int accepts,
395           const char *delim,
396           const char *ignore,
397           char *tbuf)
398 {
399         char    c;
400         int     escape, quote;
401
402         /* 
403          * Find the first occurance of delim, recording how many
404          * characters lead up to it.  Ignore indicated characters.
405          */
406         escape = quote = 0;
407         while ((c = *buf) != '\0') {
408                 buf++;
409                 if (!escape) {
410                         if (c == '\\') {
411                                 escape = 1;
412                                 continue;
413                         }
414                         if (c == '\"') {
415                                 quote ^= 1;
416                                 continue;
417                         }
418                         if (!quote) {
419                                 if (strchr(delim, c) != NULL) {
420                                         accepts = 1;
421                                         break;
422                                 }
423                                 if (strchr(ignore, c) != NULL)
424                                         continue;
425                         }
426                 } else
427                         escape = 0;
428                 *tbuf++ = c;
429         }
430         if (!accepts)
431                 return NULL;
432         *tbuf = '\0';                                           /* NUL term */
433         return buf;
434 }
435
436 /*
437  * Parse and record named arguments given as `name = value', comma-separated
438  * pairs.
439  *
440  * NB: Alters the passed buffer.
441  */
442 char *
443 _sysio_get_args(char *buf, struct option_value_info *vec)
444 {
445         char    *nxt;
446         char    *name, *value;
447         struct option_value_info *v;
448
449         for (;;) {
450                 nxt =
451                     (char *)_sysio_get_token(buf,
452                                              1,
453                                              "=,",
454                                              IGNORE_WHITE,
455                                              name = buf);
456                 if (!nxt ||
457                     (nxt != buf && *name == '\0' && buf + strlen(buf) == nxt)) {
458                         buf = NULL;
459                         break;
460                 }
461                 if (*name == '\0')
462                         break;
463                 buf =
464                     (char *)_sysio_get_token(nxt,
465                                              1,
466                                              ",",
467                                              IGNORE_WHITE,
468                                              value = nxt);
469                 if (*value == '\0')
470                         value = NULL;
471                 for (v = vec; v->ovi_name; v++)
472                         if (strcmp(v->ovi_name, name) == 0)
473                                 break;
474                 if (!v->ovi_name)
475                         return NULL;
476                 v->ovi_value = value;
477         }
478
479         return buf;
480 }
481
482 static int
483 parse_mm(const char *s, dev_t *devp)
484 {
485         unsigned long ul;
486         char    *cp;
487         dev_t   dev;
488
489         ul = strtoul(s, &cp, 0);
490         if (*cp != '+' || ul > USHRT_MAX)
491                 return -EINVAL;
492         dev = ul << 16;
493         s = (const char *)++cp;
494         ul = strtoul(s, &cp, 0);
495         if (*cp != '\0' || ul > USHRT_MAX)
496                 return -EINVAL;
497         dev |= ul & 0xffff;
498         *devp = dev;
499         return 0;
500 }
501
502 /*
503  * Performs the creat command for the namespace assembly
504  *
505  * NB: Alters the passed buffer.
506  */
507 static int 
508 do_creat(char *args) 
509 {
510         size_t  len;
511         struct option_value_info v[] = {
512                 { "ft",         NULL },                 /* file type */
513                 { "nm",         NULL },                 /* name */
514                 { "pm",         NULL },                 /* permissions */
515                 { "ow",         NULL },                 /* owner */
516                 { "gr",         NULL },                 /* group */
517                 { "mm",         NULL },                 /* major + minor */
518                 { "str",        NULL },                 /* file data */
519                 { NULL,         NULL }
520         };
521         const char *cp;
522         long    perms;
523         long    owner, group;
524         struct pnode *dir, *pno;
525         mode_t  mode;
526         struct intent intent;
527         dev_t   dev;
528         int     err;
529   
530         len = strlen(args);
531         if (_sysio_get_args(args, v) - args != (ssize_t )len ||
532             !(v[0].ovi_value &&
533               v[1].ovi_value &&
534               v[2].ovi_value))
535                 return -EINVAL;
536         perms = strtol(v[2].ovi_value, (char **)&cp, 0);
537         if (*cp ||
538             perms < 0 ||
539             (perms == LONG_MAX && errno == ERANGE) ||
540             ((unsigned)perms & ~07777))
541                 return -EINVAL;
542         if (v[3].ovi_value) {
543                 owner = strtol(v[3].ovi_value, (char **)&cp, 0);
544                 if (*cp ||
545                     ((owner == LONG_MIN || owner == LONG_MAX)
546                      && errno == ERANGE))
547                         return -EINVAL;
548         } else
549                 owner = getuid();
550         if (v[4].ovi_value) {
551                 group = strtol(v[4].ovi_value, (char **)&cp, 0);
552                 if (*cp ||
553                     ((group == LONG_MIN || group == LONG_MAX) &&
554                      errno == ERANGE))
555                         return -EINVAL;
556         } else
557                 group = getegid();
558
559         if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
560                 return -ENOENT;
561         err = 0;
562         mode = perms;
563         if (strcmp(v[0].ovi_value, "dir") == 0) {
564                 INTENT_INIT(&intent, INT_CREAT, &mode, 0);
565                 err =
566                     _sysio_namei(dir, v[1].ovi_value, ND_NEGOK, &intent, &pno);
567                 if (err)
568                         return err;
569                 if (pno->p_base->pb_ino)
570                         err = -EEXIST;
571                 else if (IS_RDONLY(pno->p_parent,
572                                    pno->p_parent->p_base->pb_ino))
573                         err = -EROFS;
574                 else {
575                         struct inode *ino;
576
577                         ino = pno->p_parent->p_base->pb_ino;
578                         err = (*ino->i_ops.inop_mkdir)(pno, mode);
579                 }
580                 P_RELE(pno);
581         } else if (strcmp(v[0].ovi_value, "chr") == 0) {
582                 if (!(v[5].ovi_value && parse_mm(v[5].ovi_value, &dev) == 0))
583                         return -EINVAL;
584                 mode |= S_IFCHR;
585                 INTENT_INIT(&intent, INT_CREAT, &mode, 0);
586                 err =
587                     _sysio_namei(dir, v[1].ovi_value, ND_NEGOK, &intent, &pno);
588                 if (err)
589                         return err;
590                 if (pno->p_base->pb_ino)
591                         err = -EEXIST;
592                 else if (IS_RDONLY(pno->p_parent,
593                                    pno->p_parent->p_base->pb_ino))
594                         err = -EROFS;
595                 else {
596                         struct inode *ino;
597
598                         ino = pno->p_parent->p_base->pb_ino;
599                         err = (*ino->i_ops.inop_mknod)(pno, mode, dev);
600                 }
601                 P_RELE(pno);
602         } else if (strcmp(v[0].ovi_value, "blk") == 0) {
603                 /*
604                  * We don't support block special files yet.
605                  */
606                 return -EINVAL;
607         } else if (strcmp(v[0].ovi_value, "file") == 0) {
608                 int     i;
609                 struct inode *ino;
610
611                 i = O_CREAT|O_EXCL;
612                 INTENT_INIT(&intent, INT_CREAT, &mode, &i);
613                 err =
614                     _sysio_namei(dir, v[1].ovi_value, ND_NEGOK, &intent, &pno);
615                 if (err)
616                         return err;
617                 err = _sysio_open(pno, O_CREAT|O_EXCL, mode);
618                 if (err) {
619                         P_RELE(pno);
620                         return err;
621                 }
622                 ino = pno->p_base->pb_ino;
623                 if (!err && v[6].ovi_value) {
624                         struct iovec iovec;
625                         struct intnl_xtvec xtvec;
626                         struct ioctx io_context;
627
628                         /*
629                          * Deposit optional file content.
630                          */
631                         iovec.iov_base = v[6].ovi_value;
632                         iovec.iov_len = strlen(v[6].ovi_value);
633                         xtvec.xtv_off = 0;
634                         xtvec.xtv_len = iovec.iov_len;
635                         IOCTX_INIT(&io_context,
636                                    1,
637                                    1,
638                                    ino,
639                                    &iovec, 1,
640                                    &xtvec, 1);
641                         _sysio_ioctx_enter(&io_context);
642                         err =
643                             (*ino->i_ops.inop_write)(pno->p_base->pb_ino,
644                                                      &io_context);
645                         if (!err) {
646                                 ssize_t cc;
647
648                                 cc = _sysio_ioctx_wait(&io_context);
649                                 if (cc < 0)
650                                         err = cc;
651                                 else if ((size_t )cc != iovec.iov_len)
652                                         err = -EIO;             /* huh? */
653                         } else
654                                 _sysio_ioctx_complete(&io_context);
655                 }
656                 i = (*ino->i_ops.inop_close)(ino);
657                 if (!err)
658                         err = i;
659                 P_RELE(pno);
660         } else 
661                 err = -EINVAL;
662
663         return err;
664 }
665
666 /*
667  * Do mount.
668  *
669  * NB: The passed buffer is altered.
670  */
671 static int 
672 do_mnt(char *args) 
673 {
674         size_t  len;
675         struct option_value_info v[] = {
676                 { "dev",        NULL },                 /* source (type:dev) */
677                 { "dir",        NULL },                 /* target dir */
678                 { "fl",         NULL },                 /* flags */
679                 { "da",         NULL },                 /* mount data */
680                 { NULL,         NULL }
681         };
682         char    *ty, *name;
683         unsigned long flags;
684         struct pnode *dir;
685   
686         len = strlen(args);
687         if (_sysio_get_args(args, v) - args != (ssize_t )len ||
688             !(v[0].ovi_value && v[1].ovi_value))
689                 return -EINVAL;
690         ty =
691             (char *)_sysio_get_token(v[0].ovi_value,
692                                      1,
693                                      ":",
694                                      "",
695                                      name = v[0].ovi_value);
696         flags = 0;
697         if (v[2].ovi_value) {
698                 char    *cp;
699
700                 /*
701                  * Optional flags.
702                  */
703                 flags = strtoul(v[2].ovi_value, &cp, 0);
704                 if (*cp || (flags == ULONG_MAX && errno == ERANGE))
705                         return -EINVAL;
706         }
707
708         if (strlen(v[1].ovi_value) == 1 && v[1].ovi_value[0] == PATH_SEPARATOR) {
709                 /*
710                  * Aha! It's root they want. Have to do that special.
711                  */
712                 return _sysio_mount_root(ty, name, flags, v[3].ovi_value);
713         }
714
715         if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
716                 return -ENOENT;
717         return _sysio_mount(dir,
718                             ty,
719                             v[1].ovi_value,
720                             name,
721                             flags,
722                             v[3].ovi_value);
723 }
724
725
726 #if 0
727 /*
728  * Chdir
729  *
730  * NB: Alters the passed buffer.
731  */
732 static int 
733 do_cd(char *args) 
734 {
735         size_t  len;
736         struct option_value_info v[] = {
737                 { "dir",        NULL },                 /* directory */
738                 { NULL,         NULL }
739         };
740         int     err;
741         struct pnode *dir, *pno;
742
743         len = strlen(args);
744         if (_sysio_get_args(args, v) - args != (ssize_t )len || !v[0].ovi_value)
745                 return -EINVAL;
746
747         if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
748                 return -ENOENT;
749         err = _sysio_namei(dir, v[0].ovi_value, 0, NULL, &pno);
750         if (err)
751                 return err;
752         err = _sysio_p_chdir(pno);
753         if (err)
754                 P_RELE(pno);
755         return err;
756 }
757 #endif
758
759 /*
760  * Does a chmod
761  *
762  * NB: Alters passed buffer.
763  */
764 static int 
765 do_chmd(char *args)
766 {
767         size_t  len;
768         struct option_value_info v[] = {
769                 { "src",        NULL },                 /* path */
770                 { "pm",         NULL },                 /* perms */
771                 { NULL,         NULL }
772         };
773         long    perms;
774         char    *cp;
775         struct intnl_stat stbuf;
776         int     err;
777         struct pnode *dir, *pno;
778   
779         len = strlen(args);
780         if (_sysio_get_args(args, v) - args != (ssize_t )len ||
781             !(v[0].ovi_value && v[1].ovi_value))
782                 return -EINVAL;
783         perms = strtol(v[1].ovi_value, &cp, 0);
784         if (*cp ||
785             perms < 0 ||
786             (perms == LONG_MAX && errno == ERANGE) ||
787             ((unsigned)perms & ~07777))
788                 return -EINVAL;
789         (void )memset(&stbuf, 0, sizeof(stbuf));
790         stbuf.st_mode = (mode_t)perms;
791
792         if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
793                 return -ENOENT;
794         err = _sysio_namei(dir, v[0].ovi_value, 0, NULL, &pno);
795         if (err)
796                 return err;
797         err = _sysio_setattr(pno, pno->p_base->pb_ino, SETATTR_MODE, &stbuf);
798         P_RELE(pno);
799
800         return err;
801 }
802
803 static int
804 do_open(char *args)
805 {
806         size_t  len;
807         struct option_value_info v[] = {
808                 { "nm",         NULL },                 /* path */
809                 { "fd",         NULL },                 /* fildes */
810                 { "m",          NULL },                 /* mode */
811                 { NULL,         NULL }
812         };
813         char    *cp;
814         long    l;
815         int     fd;
816         unsigned long ul;
817         mode_t  m;
818         struct pnode *dir, *pno;
819         struct intent intent;
820         int     err;
821         struct file *fil;
822
823         len = strlen(args);
824         if (_sysio_get_args(args, v) - args != (ssize_t )len ||
825             !(v[0].ovi_value && v[1].ovi_value && v[2].ovi_value))
826                 return -EINVAL;
827         l = strtol(v[1].ovi_value, (char **)&cp, 0);
828         if (*cp || l < 0 || _irecheck(l, errno))
829                 return -EINVAL;
830         fd = (int )l;
831         ul = strtoul(v[1].ovi_value, (char **)&cp, 0);
832         if (*cp ||
833             (ul == ULONG_MAX && errno == ERANGE))
834                 return -EINVAL;
835         m = (mode_t )ul & (O_RDONLY|O_WRONLY|O_RDWR);
836
837         if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
838                 return -ENOENT;
839         INTENT_INIT(&intent, INT_OPEN, &m, NULL);
840         pno = NULL;
841         err = _sysio_namei(dir, v[0].ovi_value, 0, &intent, &pno);
842         if (err)
843                 return err;
844         fil = NULL;
845         do {
846                 err = _sysio_open(pno, m, 0);
847                 if (err)
848                         break;
849                 fil = _sysio_fnew(pno->p_base->pb_ino, m);
850                 if (!fil) {
851                         err = -ENOMEM;
852                         break;
853                 }
854                 err = _sysio_fd_set(fil, fd, 1);
855                 if (err < 0)
856                         break;
857                 P_RELE(pno);
858                 return 0;
859         } while (0);
860         if (fil)
861                 F_RELE(fil);
862         if (pno)
863                 P_RELE(pno);
864         return err;
865 }
866
867 /*
868  * Execute the given cmd.
869  *
870  * NB: Buf is altered.
871  */
872 static int 
873 do_command(char *buf)
874 {
875         size_t  len;
876         char    *args, *cmd;
877
878         len = strlen(buf);
879         args = (char *)_sysio_get_token(buf, 1, ",", IGNORE_WHITE, cmd = buf);
880         if (args) {
881                 if (strcmp("creat", cmd) == 0)
882                         return do_creat(args);
883                 if (strcmp("mnt", cmd) == 0)
884                         return do_mnt(args);
885 #if 0
886                 if (strcmp("cd", cmd) == 0)
887                         return do_cd(args);
888 #endif
889                 if (strcmp("chmd", cmd) == 0)
890                         return do_chmd(args);
891                 if (strcmp("open", cmd) == 0)
892                         return do_open(args);
893         }
894         return -EINVAL;
895 }
896
897 #if SYSIO_TRACING
898 /*
899  * Set/Unset tracing.
900  */
901 static int
902 _sysio_boot_tracing(const char *arg)
903 {
904         long    l;
905         char    *cp;
906         static struct trace_callback
907                 *entcb = NULL,
908                 *exitcb = NULL;
909
910         l = 0;
911         if (arg) {
912                 l = strtol(arg, (char **)&cp, 0);
913                 if (*cp || !(l == 0 || l == 1))
914                         return -EINVAL;
915         }
916         if (l) {
917                 if (entcb == NULL)
918                         entcb =
919                             _sysio_register_trace(_sysio_entry_trace_q,
920                                                   _sysio_trace_entry);
921                 if (entcb == NULL)
922                         return -errno;
923                 if (exitcb == NULL)
924                         exitcb =
925                             _sysio_register_trace(_sysio_exit_trace_q,
926                                                   _sysio_trace_exit);
927                 if (exitcb == NULL)
928                         return -errno;
929         } else {
930                 if (entcb != NULL)
931                         _sysio_remove_trace(_sysio_entry_trace_q, entcb);
932                 entcb = NULL;
933                 if (exitcb != NULL)
934                         _sysio_remove_trace(_sysio_exit_trace_q, exitcb);
935                 exitcb = NULL;
936         }
937         return 0;
938 }
939 #endif
940
941 /*
942  * Initialize the namespace.
943  */
944 static int
945 _sysio_boot_namespace(const char *arg)
946 {
947         char    c, *tok;
948         ssize_t len;
949         int     err;
950         unsigned count;
951         /*
952          * Allocate token buffer.
953          */
954         len = strlen(arg);
955         tok = malloc(len ? len : 1);
956         if (!tok)
957                 return -ENOMEM;
958         err = 0;
959         count = 0;
960         while (1) {
961                 /*
962                  * Discard leading white space.
963                  */
964                 while ((c = *arg) != '\0' && strchr(IGNORE_WHITE, c))
965                         arg++;
966                 if (COMMENT_INTRO == c) {
967                         while (*arg && (*arg != '\n')) {
968                                 ++arg;
969                         }
970
971                         continue;
972                 }
973                 if (c == '\0')
974                         break;
975                 if (c != '{') {
976                         err = -EINVAL;
977                         break;
978                 }
979                 /*
980                  * Get the command.
981                  */
982                 *tok = '\0';
983                 arg =
984                     (char *)_sysio_get_token(arg + 1,
985                                              0,
986                                              "}",
987                                              IGNORE_WHITE,
988                                              tok);
989                 if (!arg) {
990                         err = -EINVAL;
991                         break;
992                 }
993                 count++;
994                 /*
995                  * Perform.
996                  */
997                 err = do_command(tok);
998                 if (err)
999                         break;
1000         }
1001 #if SYSIO_TRACING
1002         if (err)
1003                 _sysio_cprintf("+NS init+ failed at expr %u (last = %s): %s\n", 
1004                                count,
1005                                tok && *tok ? tok : "NULL",
1006                                strerror(-err));
1007 #endif
1008         free(tok);
1009         return err;
1010 }
1011
1012 #if DEFER_INIT_CWD
1013 /*
1014  * Set deferred initial working directory.
1015  */
1016 static int
1017 _sysio_boot_cwd(const char *arg)
1018 {
1019
1020         _sysio_init_cwd = arg;
1021         return 0;
1022 }
1023 #endif
1024
1025 /*
1026  * Given an identifier and it's arguments, perform optional initializations.
1027  */
1028 int 
1029 _sysio_boot(const char *opt, const char *arg)
1030 {
1031         struct option_value_info vec[] = {
1032 #if SYSIO_TRACING
1033                 { "trace",      NULL },                 /* tracing? */
1034 #endif
1035                 { "namespace",  NULL },                 /* init namespace? */
1036 #if DEFER_INIT_CWD
1037                 { "cwd",        NULL },                 /* init working dir */
1038 #endif
1039                 { NULL,         NULL }
1040         };
1041         struct option_value_info *v;
1042         unsigned u;
1043         static int (*f[])(const char *) = {
1044 #if SYSIO_TRACING
1045                 _sysio_boot_tracing,
1046 #endif
1047                 _sysio_boot_namespace,
1048 #if DEFER_INIT_CWD
1049                 _sysio_boot_cwd,
1050 #endif
1051                 NULL                                    /* can't happen */
1052         };
1053
1054         for (v = vec, u = 0; v->ovi_name; v++, u++)
1055                 if (strcmp(v->ovi_name, opt) == 0)
1056                         break;
1057         if (!v->ovi_name)
1058                 return -EINVAL;
1059         return (*f[u])(arg);
1060 }