Whamcloud - gitweb
new upstream libsysio snapshot (20041101)
[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-2004 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 #include <stdlib.h>
47 #include <unistd.h>
48 #include <string.h>
49 #include <errno.h>
50 #include <limits.h>
51 #include <assert.h>
52 #include <sys/types.h>
53 #include <sys/stat.h>
54 #include <fcntl.h>
55 #include <sys/uio.h>
56 #include <sys/queue.h>
57
58 #include "xtio.h"
59 #include "sysio.h"
60 #include "inode.h"
61 #include "fs.h"
62 #include "mount.h"
63 #include "file.h"
64 #include "dev.h"
65
66 #ifdef STDFD_DEV
67 #include "stdfd.h"
68 #endif
69
70 /*
71  * White space characters.
72  */
73 #define IGNORE_WHITE            " \t\r\n"
74
75 /*
76  * Sysio library initialization. Must be called before anything else in the
77  * library.
78  */
79 int
80 _sysio_init()
81 {
82         int     err;
83 #ifdef WITH_SOCKETS
84         int     _sysio_sockets_init(void);
85 #endif
86
87         err = _sysio_ioctx_init();
88         if (err)
89                 goto error;
90         err = _sysio_i_init();
91         if (err)
92                 goto error;
93         err = _sysio_mount_init();
94         if (err)
95                 goto error;
96
97         err = _sysio_dev_init();
98         if (err)
99                 goto error;
100 #ifdef STDFD_DEV
101         err = _sysio_stdfd_init();
102         if (err)
103                 goto error;
104 #endif
105 #ifdef WITH_SOCKETS
106         err = _sysio_sockets_init();
107         if (err)
108                 goto error;
109 #endif
110
111         goto out;
112 error:
113         errno = -err;
114 out:
115         /*
116          * Unlike all other _sysio routines, this one returns with errno
117          * set. It also returns the error, as usual.
118          */
119         return err;
120 }
121
122 /*
123  * Sysio library shutdown.
124  */
125 void
126 _sysio_shutdown()
127 {
128
129         if (!(_sysio_fd_close_all() == 0 &&
130               _sysio_unmount_all() == 0))
131                         abort();
132
133 #if ZERO_SUM_MEMORY
134         _sysio_fd_shutdown();
135         _sysio_i_shutdown();
136         _sysio_fssw_shutdown();
137 #endif
138 }
139
140 /* 
141  * (kind of)Duplicates strtok function.
142  *
143  * Given a buffer, returns the longest string
144  * that does not contain any delim characters.  Will
145  * remove ws and any characters in the ignore string.  
146  * Returns the token.  
147  *
148  * The parameter controlling acceptance controls whether a positive
149  * match for some delimiter be made or not. If set, then either a delimiter
150  * or NUL character is success.
151  *
152  */
153 const char *
154 _sysio_get_token(const char *buf,
155           int accepts,
156           const char *delim,
157           const char *ignore,
158           char *tbuf)
159 {
160         char    c;
161         int     escape, quote;
162
163         /* 
164          * Find the first occurance of delim, recording how many
165          * characters lead up to it.  Ignore indicated characters.
166          */
167         escape = quote = 0;
168         while ((c = *buf) != '\0') {
169                 buf++;
170                 if (!escape) {
171                         if (c == '\\') {
172                                 escape = 1;
173                                 continue;
174                         }
175                         if (c == '\"') {
176                                 quote ^= 1;
177                                 continue;
178                         }
179                         if (!quote) {
180                                 if (strchr(delim, c) != NULL) {
181                                         accepts = 1;
182                                         break;
183                                 }
184                                 if (strchr(ignore, c) != NULL)
185                                         continue;
186                         }
187                 } else
188                         escape = 0;
189                 *tbuf++ = c;
190         }
191         if (!accepts)
192                 return NULL;
193         *tbuf = '\0';                                           /* NUL term */
194         return buf;
195 }
196
197 /*
198  * Parse and record named arguments given as `name = value', comma-separated
199  * pairs.
200  *
201  * NB: Alters the passed buffer.
202  */
203 char *
204 _sysio_get_args(char *buf, struct option_value_info *vec)
205 {
206         char    *nxt;
207         char    *name, *value;
208         struct option_value_info *v;
209
210         for (;;) {
211                 nxt =
212                     (char *)_sysio_get_token(buf,
213                                              1,
214                                              "=,",
215                                              IGNORE_WHITE,
216                                              name = buf);
217                 if (!nxt ||
218                     (nxt != buf && *name == '\0' && buf + strlen(buf) == nxt)) {
219                         buf = NULL;
220                         break;
221                 }
222                 if (*name == '\0')
223                         break;
224                 buf =
225                     (char *)_sysio_get_token(nxt,
226                                              1,
227                                              ",",
228                                              IGNORE_WHITE,
229                                              value = nxt);
230                 if (*value == '\0')
231                         value = NULL;
232                 for (v = vec; v->ovi_name; v++)
233                         if (strcmp(v->ovi_name, name) == 0)
234                                 break;
235                 if (!v->ovi_name)
236                         return NULL;
237                 v->ovi_value = value;
238         }
239
240         return buf;
241 }
242
243 static int
244 parse_mm(const char *s, dev_t *devp)
245 {
246         unsigned long ul;
247         char    *cp;
248         dev_t   dev;
249
250         ul = strtoul(s, &cp, 0);
251         if (*cp != '+' || ul > USHRT_MAX)
252                 return -EINVAL;
253         dev = ul << 16;
254         s = (const char *)++cp;
255         ul = strtoul(s, &cp, 0);
256         if (*cp != '\0' || ul > USHRT_MAX)
257                 return -EINVAL;
258         dev |= ul & 0xffff;
259         *devp = dev;
260         return 0;
261 }
262
263 /*
264  * Performs the creat command for the namespace assembly
265  *
266  * NB: Alters the passed buffer.
267  */
268 static int 
269 do_creat(char *args) 
270 {
271         size_t  len;
272         struct option_value_info v[] = {
273                 { "ft",         NULL },                 /* file type */
274                 { "nm",         NULL },                 /* name */
275                 { "pm",         NULL },                 /* permissions */
276                 { "ow",         NULL },                 /* owner */
277                 { "gr",         NULL },                 /* group */
278                 { "mm",         NULL },                 /* major + minor */
279                 { "str",        NULL },                 /* file data */
280                 { NULL,         NULL }
281         };
282         const char *cp;
283         long    perms;
284         long    owner, group;
285         struct pnode *dir, *pno;
286         mode_t  mode;
287         struct intent intent;
288         dev_t   dev;
289         int     err;
290   
291         len = strlen(args);
292         if (_sysio_get_args(args, v) - args != (ssize_t )len ||
293             !(v[0].ovi_value &&
294               v[1].ovi_value &&
295               v[2].ovi_value))
296                 return -EINVAL;
297         perms = strtol(v[2].ovi_value, (char **)&cp, 0);
298         if (*cp ||
299             perms < 0 ||
300             (perms == LONG_MAX && errno == ERANGE) ||
301             ((unsigned)perms & ~07777))
302                 return -EINVAL;
303         if (v[3].ovi_value) {
304                 owner = strtol(v[3].ovi_value, (char **)&cp, 0);
305                 if (*cp ||
306                     ((owner == LONG_MIN || owner == LONG_MAX)
307                      && errno == ERANGE))
308                         return -EINVAL;
309         } else
310                 owner = getuid();
311         if (v[4].ovi_value) {
312                 group = strtol(v[4].ovi_value, (char **)&cp, 0);
313                 if (*cp ||
314                     ((group == LONG_MIN || group == LONG_MAX) &&
315                      errno == ERANGE))
316                         return -EINVAL;
317         } else
318                 group = getegid();
319
320         if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
321                 return -ENOENT;
322         err = 0;
323         mode = perms;
324         if (strcmp(v[0].ovi_value, "dir") == 0) {
325                 INTENT_INIT(&intent, INT_CREAT, &mode, 0);
326                 err =
327                     _sysio_namei(dir, v[1].ovi_value, ND_NEGOK, &intent, &pno);
328                 if (err)
329                         return err;
330                 if (pno->p_base->pb_ino)
331                         err = -EEXIST;
332                 else if (IS_RDONLY(pno->p_parent,
333                                    pno->p_parent->p_base->pb_ino))
334                         err = -EROFS;
335                 else {
336                         struct inode *ino;
337
338                         ino = pno->p_parent->p_base->pb_ino;
339                         err = (*ino->i_ops.inop_mkdir)(pno, mode);
340                 }
341                 P_RELE(pno);
342         } else if (strcmp(v[0].ovi_value, "chr") == 0) {
343                 if (!(v[5].ovi_value && parse_mm(v[5].ovi_value, &dev) == 0))
344                         return -EINVAL;
345                 mode |= S_IFCHR;
346                 INTENT_INIT(&intent, INT_CREAT, &mode, 0);
347                 err =
348                     _sysio_namei(dir, v[1].ovi_value, ND_NEGOK, &intent, &pno);
349                 if (err)
350                         return err;
351                 if (pno->p_base->pb_ino)
352                         err = -EEXIST;
353                 else if (IS_RDONLY(pno->p_parent,
354                                    pno->p_parent->p_base->pb_ino))
355                         err = -EROFS;
356                 else {
357                         struct inode *ino;
358
359                         ino = pno->p_parent->p_base->pb_ino;
360                         err = (*ino->i_ops.inop_mknod)(pno, mode, dev);
361                 }
362                 P_RELE(pno);
363         } else if (strcmp(v[0].ovi_value, "blk") == 0) {
364                 /*
365                  * We don't support block special files yet.
366                  */
367                 return -EINVAL;
368         } else if (strcmp(v[0].ovi_value, "file") == 0) {
369                 int     i;
370                 struct inode *ino;
371
372                 i = O_CREAT|O_EXCL;
373                 INTENT_INIT(&intent, INT_CREAT, &mode, &i);
374                 err =
375                     _sysio_namei(dir, v[1].ovi_value, ND_NEGOK, &intent, &pno);
376                 if (err)
377                         return err;
378                 err = _sysio_open(pno, O_CREAT|O_EXCL, mode);
379                 if (err) {
380                         P_RELE(pno);
381                         return err;
382                 }
383                 ino = pno->p_base->pb_ino;
384                 if (!err && v[6].ovi_value) {
385                         struct iovec iovec;
386                         struct intnl_xtvec xtvec;
387                         struct ioctx io_context;
388
389                         /*
390                          * Deposit optional file content.
391                          */
392                         iovec.iov_base = v[6].ovi_value;
393                         iovec.iov_len = strlen(v[6].ovi_value);
394                         xtvec.xtv_off = 0;
395                         xtvec.xtv_len = iovec.iov_len;
396                         IOCTX_INIT(&io_context,
397                                    1,
398                                    1,
399                                    ino,
400                                    &iovec, 1,
401                                    &xtvec, 1);
402                         _sysio_ioctx_enter(&io_context);
403                         err =
404                             (*ino->i_ops.inop_write)(pno->p_base->pb_ino,
405                                                      &io_context);
406                         if (!err) {
407                                 ssize_t cc;
408
409                                 cc = _sysio_ioctx_wait(&io_context);
410                                 if (cc < 0)
411                                         err = cc;
412                                 else if ((size_t )cc != iovec.iov_len)
413                                         err = -EIO;             /* huh? */
414                         } else
415                                 _sysio_ioctx_complete(&io_context);
416                 }
417                 i = (*ino->i_ops.inop_close)(ino);
418                 if (!err)
419                         err = i;
420                 P_RELE(pno);
421         } else 
422                 err = -EINVAL;
423
424         return err;
425 }
426
427 /*
428  * Do mount.
429  *
430  * NB: The passed buffer is altered.
431  */
432 static int 
433 do_mnt(char *args) 
434 {
435         size_t  len;
436         struct option_value_info v[] = {
437                 { "dev",        NULL },                 /* source (type:dev) */
438                 { "dir",        NULL },                 /* target dir */
439                 { "fl",         NULL },                 /* flags */
440                 { "da",         NULL },                 /* mount data */
441                 { NULL,         NULL }
442         };
443         char    *ty, *name;
444         unsigned long flags;
445         struct pnode *dir;
446   
447         len = strlen(args);
448         if (_sysio_get_args(args, v) - args != (ssize_t )len ||
449             !(v[0].ovi_value && v[1].ovi_value))
450                 return -EINVAL;
451         ty =
452             (char *)_sysio_get_token(v[0].ovi_value,
453                                      1,
454                                      ":",
455                                      "",
456                                      name = v[0].ovi_value);
457         flags = 0;
458         if (v[2].ovi_value) {
459                 char    *cp;
460
461                 /*
462                  * Optional flags.
463                  */
464                 flags = strtoul(v[2].ovi_value, &cp, 0);
465                 if (*cp || (flags == ULONG_MAX && errno == ERANGE))
466                         return -EINVAL;
467         }
468
469         if (strlen(v[1].ovi_value) == 1 && v[1].ovi_value[0] == PATH_SEPARATOR) {
470                 /*
471                  * Aha! It's root they want. Have to do that special.
472                  */
473                 return _sysio_mount_root(ty, name, flags, v[3].ovi_value);
474         }
475
476         if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
477                 return -ENOENT;
478         return _sysio_mount(dir,
479                             ty,
480                             v[1].ovi_value,
481                             name,
482                             flags,
483                             v[3].ovi_value);
484 }
485
486
487 #if 0
488 /*
489  * Chdir
490  *
491  * NB: Alters the passed buffer.
492  */
493 static int 
494 do_cd(char *args) 
495 {
496         size_t  len;
497         struct option_value_info v[] = {
498                 { "dir",        NULL },                 /* directory */
499                 { NULL,         NULL }
500         };
501         int     err;
502         struct pnode *dir, *pno;
503
504         len = strlen(args);
505         if (_sysio_get_args(args, v) - args != (ssize_t )len || !v[0].ovi_value)
506                 return -EINVAL;
507
508         if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
509                 return -ENOENT;
510         err = _sysio_namei(dir, v[0].ovi_value, 0, NULL, &pno);
511         if (err)
512                 return err;
513         err = _sysio_p_chdir(pno);
514         if (err)
515                 P_RELE(pno);
516         return err;
517 }
518 #endif
519
520 /*
521  * Does a chmod
522  *
523  * NB: Alters passed buffer.
524  */
525 static int 
526 do_chmd(char *args)
527 {
528         size_t  len;
529         struct option_value_info v[] = {
530                 { "src",        NULL },                 /* path */
531                 { "pm",         NULL },                 /* perms */
532                 { NULL,         NULL }
533         };
534         long    perms;
535         char    *cp;
536         struct intnl_stat stbuf;
537         int     err;
538         struct pnode *dir, *pno;
539   
540         len = strlen(args);
541         if (_sysio_get_args(args, v) - args != (ssize_t )len ||
542             !(v[0].ovi_value && v[1].ovi_value))
543                 return -EINVAL;
544         perms = strtol(v[1].ovi_value, &cp, 0);
545         if (*cp ||
546             perms < 0 ||
547             (perms == LONG_MAX && errno == ERANGE) ||
548             ((unsigned)perms & ~07777))
549                 return -EINVAL;
550         (void )memset(&stbuf, 0, sizeof(stbuf));
551         stbuf.st_mode = (mode_t)perms;
552
553         if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
554                 return -ENOENT;
555         err = _sysio_namei(dir, v[0].ovi_value, 0, NULL, &pno);
556         if (err)
557                 return err;
558         err = _sysio_setattr(pno, pno->p_base->pb_ino, SETATTR_MODE, &stbuf);
559         P_RELE(pno);
560
561         return err;
562 }
563
564 static int
565 do_open(char *args)
566 {
567         size_t  len;
568         struct option_value_info v[] = {
569                 { "nm",         NULL },                 /* path */
570                 { "fd",         NULL },                 /* fildes */
571                 { "m",          NULL },                 /* mode */
572                 { NULL,         NULL }
573         };
574         char    *cp;
575         long    l;
576         int     fd;
577         unsigned long ul;
578         mode_t  m;
579         struct pnode *dir, *pno;
580         struct intent intent;
581         int     err;
582         struct file *fil;
583
584 /*
585  * Check if long overflows integer range.
586  */
587 #if LONG_MAX <= INT_MAX
588 #define _irecheck(_l, _e) \
589         ((_l) == LONG_MAX && (_e) == ERANGE)
590 #else
591 #define _irecheck(_l, _e) \
592         ((_l) > INT_MAX)
593 #endif
594
595         len = strlen(args);
596         if (_sysio_get_args(args, v) - args != (ssize_t )len ||
597             !(v[0].ovi_value && v[1].ovi_value && v[2].ovi_value))
598                 return -EINVAL;
599         l = strtol(v[1].ovi_value, (char **)&cp, 0);
600         if (*cp || l < 0 || _irecheck(l, errno))
601                 return -EINVAL;
602         fd = (int )l;
603         ul = strtoul(v[1].ovi_value, (char **)&cp, 0);
604         if (*cp ||
605             (ul == ULONG_MAX && errno == ERANGE))
606                 return -EINVAL;
607         m = (mode_t )ul & (O_RDONLY|O_WRONLY|O_RDWR);
608
609         if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
610                 return -ENOENT;
611         INTENT_INIT(&intent, INT_OPEN, &m, NULL);
612         pno = NULL;
613         err = _sysio_namei(dir, v[0].ovi_value, 0, &intent, &pno);
614         if (err)
615                 return err;
616         fil = NULL;
617         do {
618                 err = _sysio_open(pno, m, 0);
619                 if (err)
620                         break;
621                 fil = _sysio_fnew(pno->p_base->pb_ino, m);
622                 if (!fil) {
623                         err = -ENOMEM;
624                         break;
625                 }
626                 err = _sysio_fd_set(fil, fd, 1);
627                 if (err < 0)
628                         break;
629                 P_RELE(pno);
630                 return 0;
631         } while (0);
632         if (fil)
633                 F_RELE(fil);
634         if (pno)
635                 P_RELE(pno);
636         return err;
637
638 #undef _irecheck
639 }
640
641 /*
642  * Execute the given cmd.
643  *
644  * NB: Buf is altered.
645  */
646 static int 
647 do_command(char *buf)
648 {
649         size_t  len;
650         char    *args, *cmd;
651
652         len = strlen(buf);
653         args = (char *)_sysio_get_token(buf, 1, ",", IGNORE_WHITE, cmd = buf);
654         if (args) {
655                 if (strcmp("creat", cmd) == 0)
656                         return do_creat(args);
657                 if (strcmp("mnt", cmd) == 0)
658                         return do_mnt(args);
659 #if 0
660                 if (strcmp("cd", cmd) == 0)
661                         return do_cd(args);
662 #endif
663                 if (strcmp("chmd", cmd) == 0)
664                         return do_chmd(args);
665                 if (strcmp("open", cmd) == 0)
666                         return do_open(args);
667         }
668         return -EINVAL;
669 }
670
671 /*
672  * Given a command sequence buffer, parse it and run the given
673  * commands 
674  */
675 int 
676 _sysio_boot(const char *buf
677 #if DEFER_INIT_CWD
678             , const char *path
679 #endif
680            )
681 {
682         char    c, *tok;
683         ssize_t len;
684         int     err;
685
686         if (!buf)
687                 buf = "";
688         /*
689          * Allocate token buffer.
690          */
691         len = strlen(buf);
692         tok = malloc(len ? len : 1);
693         if (!tok)
694                 return -ENOMEM;
695         err = 0;
696         while (1) {
697                 /*
698                  * Discard leading white space.
699                  */
700                 while ((c = *buf) != '\0' &&
701                        !(c == '{' || strchr(IGNORE_WHITE, c) == NULL))
702                         buf++;
703                 if (c == '\0')
704                         break;
705                 if (c != '{') {
706                         err = -EINVAL;
707                         break;
708                 }
709                 /*
710                  * Get the command.
711                  */
712                 buf =
713                     (char *)_sysio_get_token(buf + 1,
714                                              0,
715                                              "}",
716                                              IGNORE_WHITE,
717                                              tok);
718                 if (!buf) {
719                         err = -EINVAL;
720                         break;
721                 }
722                 /*
723                  * Perform.
724                  */
725                 err = do_command(tok);
726                 if (err)
727                         break;
728         }
729         free(tok);
730 #if DEFER_INIT_CWD
731         if (err)
732                 return err;
733         _sysio_init_cwd = path;
734 #endif
735         return err;
736 }