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