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