Whamcloud - gitweb
import older libsysio snapshot.
[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 <sys/queue.h>
55 #include <sys/uio.h>
56
57 #include <sys/stat.h>
58 #include <fcntl.h>
59
60 #include "sysio.h"
61 #include "inode.h"
62 #include "fs.h"
63 #include "mount.h"
64 #include "file.h"
65 #include "dev.h"
66 #include "xtio.h"
67
68 #ifdef STDFD_DEV
69 #include "stdfd.h"
70 #endif
71
72 /*
73  * The namespace assembly buffer passes args with a `name'=`value'
74  * syntax. We use the following to record that in various
75  * routines below.
76  */
77 struct named_argument {
78         const char *name;                               /* arg name */
79         char    *value;                                 /* arg value */
80 };
81
82 /*
83  * White space characters.
84  */
85 #define IGNORE_WHITE            " \t\r\n"
86
87 /*
88  * Sysio library initialization. Must be called before anything else in the
89  * library.
90  */
91 int
92 _sysio_init()
93 {
94         int     err;
95 #ifdef WITH_SOCKETS
96         int     _sysio_sockets_init(void);
97 #endif
98
99         err = _sysio_ioctx_init();
100         if (err)
101                 goto error;
102         err = _sysio_i_init();
103         if (err)
104                 goto error;
105         err = _sysio_mount_init();
106         if (err)
107                 goto error;
108
109         err = _sysio_dev_init();
110         if (err)
111                 goto error;
112 #ifdef STDFD_DEV
113         err = _sysio_stdfd_init();
114         if (err)
115                 goto error;
116 #endif
117 #ifdef WITH_SOCKETS
118         err = _sysio_sockets_init();
119         if (err)
120                 goto error;
121 #endif
122
123         goto out;
124 error:
125         errno = -err;
126 out:
127         /*
128          * Unlike all other _sysio routines, this one returns with errno
129          * set. It also returns the error, as usual.
130          */
131         return err;
132 }
133
134 /*
135  * Sysio library shutdown.
136  */
137 void
138 _sysio_shutdown()
139 {
140
141         if (!(_sysio_fd_close_all() == 0 &&
142               _sysio_unmount_all() == 0))
143                         abort();
144
145 #if ZERO_SUM_MEMORY
146         _sysio_fd_shutdown();
147         _sysio_i_shutdown();
148         _sysio_fssw_shutdown();
149 #endif
150 }
151
152 /* 
153  * (kind of)Duplicates strtok function.
154  *
155  * Given a buffer, returns the longest string
156  * that does not contain any delim characters.  Will
157  * remove ws and any characters in the ignore string.  
158  * Returns the token.  
159  *
160  * The parameter controlling acceptance controls whether a positive
161  * match for some delimiter be made or not. If set, then either a delimiter
162  * or NUL character is success.
163  *
164  */
165 static const char *
166 get_token(const char *buf,
167           int accepts,
168           const char *delim,
169           const char *ignore,
170           char *tbuf)
171 {
172         char    c;
173         int     escape, quote;
174
175         /* 
176          * Find the first occurance of delim, recording how many
177          * characters lead up to it.  Ignore indicated characters.
178          */
179         escape = quote = 0;
180         while ((c = *buf) != '\0') {
181                 buf++;
182                 if (!escape) {
183                         if (c == '\\') {
184                                 escape = 1;
185                                 continue;
186                         }
187                         if (c == '\"') {
188                                 quote ^= 1;
189                                 continue;
190                         }
191                         if (!quote) {
192                                 if (strchr(delim, c) != NULL) {
193                                         accepts = 1;
194                                         break;
195                                 }
196                                 if (strchr(ignore, c) != NULL)
197                                         continue;
198                         }
199                 } else
200                         escape = 0;
201                 *tbuf++ = c;
202         }
203         if (!accepts)
204                 return NULL;
205         *tbuf = '\0';                                           /* NUL term */
206         return buf;
207 }
208
209 /*
210  * Parse and record named arguments given as `name = value', comma-separated
211  * pairs.
212  *
213  * NB: Alters the passed buffer.
214  */
215 static char *
216 get_args(char *buf, struct named_argument *vec)
217 {
218         char    *nxt;
219         char    *name, *value;
220         struct named_argument *v;
221
222         for (;;) {
223                 nxt = (char *)get_token(buf, 1, "=,", IGNORE_WHITE, name = buf);
224                 if (!nxt ||
225                     (nxt != buf && *name == '\0' && buf + strlen(buf) == nxt)) {
226                         buf = NULL;
227                         break;
228                 }
229                 if (*name == '\0')
230                         break;
231                 buf = (char *)get_token(nxt, 1, ",", IGNORE_WHITE, value = nxt);
232                 if (*value == '\0')
233                         value = NULL;
234                 for (v = vec; v->name; v++)
235                         if (strcmp(v->name, name) == 0)
236                                 break;
237                 if (!v->name)
238                         return NULL;
239                 v->value = value;
240         }
241
242         return buf;
243 }
244
245 static int
246 parse_mm(const char *s, dev_t *devp)
247 {
248         unsigned long ul;
249         char    *cp;
250         dev_t   dev;
251
252         ul = strtoul(s, &cp, 0);
253         if (*cp != '+' || ul > USHRT_MAX)
254                 return -EINVAL;
255         dev = ul << 16;
256         s = (const char *)++cp;
257         ul = strtoul(s, &cp, 0);
258         if (*cp != '\0' || ul > USHRT_MAX)
259                 return -EINVAL;
260         dev |= ul & 0xffff;
261         *devp = dev;
262         return 0;
263 }
264
265 /*
266  * Performs the creat command for the namespace assembly
267  *
268  * NB: Alters the passed buffer.
269  */
270 static int 
271 do_creat(char *args) 
272 {
273         size_t  len;
274         struct named_argument v[] = {
275                 { "ft",         NULL },                 /* file type */
276                 { "nm",         NULL },                 /* name */
277                 { "pm",         NULL },                 /* permissions */
278                 { "ow",         NULL },                 /* owner */
279                 { "gr",         NULL },                 /* group */
280                 { "mm",         NULL },                 /* major + minor */
281                 { "str",        NULL },                 /* file data */
282                 { NULL,         NULL }
283         };
284         const char *cp;
285         long    perms;
286         long    owner, group;
287         struct pnode *dir, *pno;
288         mode_t  mode;
289         struct intent intent;
290         dev_t   dev;
291         int     err;
292   
293         len = strlen(args);
294         if (get_args(args, v) - args != (ssize_t )len ||
295             !(v[0].value &&
296               v[1].value &&
297               v[2].value))
298                 return -EINVAL;
299         perms = strtol(v[2].value, (char **)&cp, 0);
300         if (*cp ||
301             perms < 0 ||
302             (perms == LONG_MAX && errno == ERANGE) ||
303             ((unsigned)perms & ~07777))
304                 return -EINVAL;
305         if (v[3].value) {
306                 owner = strtol(v[3].value, (char **)&cp, 0);
307                 if (*cp ||
308                     ((owner == LONG_MIN || owner == LONG_MAX)
309                      && errno == ERANGE))
310                         return -EINVAL;
311         } else
312                 owner = getuid();
313         if (v[4].value) {
314                 group = strtol(v[4].value, (char **)&cp, 0);
315                 if (*cp ||
316                     ((group == LONG_MIN || group == LONG_MAX) &&
317                      errno == ERANGE))
318                         return -EINVAL;
319         } else
320                 group = getegid();
321
322         if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
323                 return -ENOENT;
324         err = 0;
325         mode = perms;
326         if (strcmp(v[0].value, "dir") == 0) {
327                 INTENT_INIT(&intent, INT_CREAT, &mode, 0);
328                 err = _sysio_namei(dir, v[1].value, ND_NEGOK, &intent, &pno);
329                 if (err)
330                         return err;
331                 if (pno->p_base->pb_ino)
332                         err = -EEXIST;
333                 else if (IS_RDONLY(pno->p_parent,
334                                    pno->p_parent->p_base->pb_ino))
335                         err = -EROFS;
336                 else {
337                         struct inode *ino;
338
339                         ino = pno->p_parent->p_base->pb_ino;
340                         err = (*ino->i_ops.inop_mkdir)(pno, mode);
341                 }
342                 P_RELE(pno);
343         } else if (strcmp(v[0].value, "chr") == 0) {
344                 if (!(v[5].value && parse_mm(v[5].value, &dev) == 0))
345                         return -EINVAL;
346                 mode |= S_IFCHR;
347                 INTENT_INIT(&intent, INT_CREAT, &mode, 0);
348                 err = _sysio_namei(dir, v[1].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].value, "blk") == 0) {
364                 /*
365                  * We don't support block special files yet.
366                  */
367                 return -EINVAL;
368         } else if (strcmp(v[0].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 = _sysio_namei(dir, v[1].value, ND_NEGOK, &intent, &pno);
375                 if (err)
376                         return err;
377                 err = _sysio_open(pno, O_CREAT|O_EXCL, mode);
378                 if (err) {
379                         P_RELE(pno);
380                         return err;
381                 }
382                 ino = pno->p_base->pb_ino;
383                 if (!err && v[6].value) {
384                         struct iovec iovec;
385                         struct intnl_xtvec xtvec;
386                         struct ioctx io_context;
387
388                         /*
389                          * Deposit optional file content.
390                          */
391                         iovec.iov_base = v[6].value;
392                         iovec.iov_len = strlen(v[6].value);
393                         xtvec.xtv_off = 0;
394                         xtvec.xtv_len = iovec.iov_len;
395                         IOCTX_INIT(&io_context,
396                                    1,
397                                    (ioid_t )&io_context,
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 named_argument 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 (get_args(args, v) - args != (ssize_t )len ||
449             !(v[0].value && v[1].value))
450                 return -EINVAL;
451         ty = (char *)get_token(v[0].value, 1, ":", "", name = v[0].value);
452         flags = 0;
453         if (v[2].value) {
454                 char    *cp;
455
456                 /*
457                  * Optional flags.
458                  */
459                 flags = strtoul(v[2].value, &cp, 0);
460                 if (*cp || (flags == ULONG_MAX && errno == ERANGE))
461                         return -EINVAL;
462         }
463
464         if (strlen(v[1].value) == 1 && v[1].value[0] == PATH_SEPARATOR) {
465                 /*
466                  * Aha! It's root they want. Have to do that special.
467                  */
468                 return _sysio_mount_root(ty, name, flags, v[3].value);
469         }
470
471         if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
472                 return -ENOENT;
473         return _sysio_mount(dir, ty, v[1].value, name, flags, v[3].value);
474 }
475
476
477 /*
478  * Chdir
479  *
480  * NB: Alters the passed buffer.
481  */
482 static int 
483 do_cd(char *args) 
484 {
485         size_t  len;
486         struct named_argument v[] = {
487                 { "dir",        NULL },                 /* directory */
488                 { NULL,         NULL }
489         };
490         int     err;
491         struct pnode *dir, *pno;
492
493         len = strlen(args);
494         if (get_args(args, v) - args != (ssize_t )len || !v[0].value)
495                 return -EINVAL;
496
497         if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
498                 return -ENOENT;
499         err = _sysio_namei(dir, v[0].value, 0, NULL, &pno);
500         if (err)
501                 return err;
502         err = _sysio_p_chdir(pno);
503         if (err)
504                 P_RELE(pno);
505         return err;
506 }
507
508 /*
509  * Does a chmod
510  *
511  * NB: Alters passed buffer.
512  */
513 static int 
514 do_chmd(char *args)
515 {
516         size_t  len;
517         struct named_argument v[] = {
518                 { "src",        NULL },                 /* path */
519                 { "pm",         NULL },                 /* perms */
520                 { NULL,         NULL }
521         };
522         long    perms;
523         char    *cp;
524         struct intnl_stat stbuf;
525         int     err;
526         struct pnode *dir, *pno;
527   
528         len = strlen(args);
529         if (get_args(args, v) - args != (ssize_t )len ||
530             !(v[0].value && v[1].value))
531                 return -EINVAL;
532         perms = strtol(v[1].value, &cp, 0);
533         if (*cp ||
534             perms < 0 ||
535             (perms == LONG_MAX && errno == ERANGE) ||
536             ((unsigned)perms & ~07777))
537                 return -EINVAL;
538         (void )memset(&stbuf, 0, sizeof(stbuf));
539         stbuf.st_mode = (mode_t)perms;
540
541         if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
542                 return -ENOENT;
543         err = _sysio_namei(dir, v[0].value, 0, NULL, &pno);
544         if (err)
545                 return err;
546         err = _sysio_setattr(pno, pno->p_base->pb_ino, SETATTR_MODE, &stbuf);
547         P_RELE(pno);
548
549         return err;
550 }
551
552 /*
553  * Execute the given cmd.
554  *
555  * NB: Buf is altered.
556  */
557 static int 
558 do_command(char *buf)
559 {
560         size_t  len;
561         char    *args, *cmd;
562
563         len = strlen(buf);
564         args = (char *)get_token(buf, 1, ",", IGNORE_WHITE, cmd = buf);
565         if (args) {
566                 if (strcmp("creat", cmd) == 0)
567                         return do_creat(args);
568                 if (strcmp("mnt", cmd) == 0)
569                         return do_mnt(args);
570                 if (strcmp("cd", cmd) == 0)
571                         return do_cd(args);
572                 if (strcmp("chmd", cmd) == 0)
573                         return do_chmd(args);
574         }
575         return -EINVAL;
576 }
577
578 /*
579  * Given a command sequence buffer, parse it and run the given
580  * commands 
581  */
582 int 
583 _sysio_boot(const char *buf) 
584 {
585         char    c, *tok;
586         int     err;
587
588         /*
589          * Allocate token buffer.
590          */
591         tok = malloc(strlen(buf));
592         if (!tok)
593                 return -ENOMEM;
594         err = 0;
595         while (1) {
596                 /*
597                  * Discard leading white space.
598                  */
599                 while ((c = *buf) != '\0' &&
600                        !(c == '{' || strchr(IGNORE_WHITE, c) == NULL))
601                         buf++;
602                 if (c == '\0')
603                         break;
604                 if (c != '{') {
605                         err = -EINVAL;
606                         break;
607                 }
608                 /*
609                  * Get the command.
610                  */
611                 buf = (char *)get_token(buf + 1, 0, "}", IGNORE_WHITE, tok);
612                 if (!buf) {
613                         err = -EINVAL;
614                         break;
615                 }
616                 /*
617                  * Perform.
618                  */
619                 err = do_command(tok);
620                 if (err)
621                         break;
622         }
623         free(tok);
624         return err;
625 }