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