openblt

a hobby OS from the late 90s
git clone http://frotz.net/git/openblt.git
Log | Files | Refs | LICENSE

vfs.c (14462B)


      1 /* Copyright 1998-1999, Sidney Cammeresi. All rights reserved.
      2 ** Distributed under the terms of the OpenBLT License
      3 */
      4 
      5 #include <stdio.h>
      6 #include <string.h>
      7 #include <stdlib.h>
      8 #include <unistd.h>
      9 #include <errno.h>
     10 #include <boot.h>
     11 #include <dlfcn.h>
     12 #include <sys/stat.h>
     13 #include <blt/syscall.h>
     14 #include <blt/namer.h>
     15 #include <blt/error.h>
     16 #include <blt/hash.h>
     17 #include <blt/libsyms.h>
     18 #include <blt/vfs.h>
     19 #include "vfs-int.h"
     20 #include "path.h"
     21 #include "shm.h"
     22 
     23 #define FG_GREEN  "\033[32m"
     24 #define FG_RED    "\033[34m"
     25 #define FG_WHITE  "\033[37m"
     26 
     27 int vfs_port;
     28 struct fs_type *fs_drivers;
     29 struct superblock *mount_list = NULL;
     30 hashtable_t *conn_table;
     31 
     32 void __libc_init_vfs (void), __dlinit (void);
     33 
     34 extern struct fs_type rootfs, bootfs;
     35 extern void __libc_init_console ();
     36 extern int __blk_ref;
     37 
     38 vfs_res_t *vfs_openconn (int rport, int area)
     39 {
     40 	int i, private_port;
     41 	vfs_res_t *res;
     42 	struct client *client;
     43 
     44 	res = malloc (sizeof (vfs_res_t));
     45 	res->status = VFS_OK;
     46 	res->errno = 0;
     47 	private_port = port_create (rport, "vfs_private_port");
     48 	port_slave (vfs_port, private_port);
     49 	res->data[0] = private_port;
     50 
     51 	client = malloc (sizeof (struct client));
     52 	client->in = rport;
     53 	client->out = private_port;
     54 	client->filename_area = area;
     55 	area_clone (area, 0, (void **) &client->nameptr, 0);
     56 	client->ioctx.cwd = malloc (BLT_MAX_NAME_LENGTH);
     57 	strcpy (client->ioctx.cwd, "/");
     58 	for (i = 0; i < MAX_FDS; i++)
     59 		client->ioctx.fdarray.ofile[i] = NULL;
     60 	hashtable_insert (conn_table, client->in, client, sizeof (struct client));
     61 
     62 #ifdef VFS_DEBUG
     63 	printf ("vfs_openconn: from port %d, assigned port %d, area %d "
     64 		"mapped to %p\n", rport, private_port, area, client->nameptr);
     65 #endif
     66 	return res;
     67 }
     68 
     69 vfs_res_t *vfs_scroll_area (struct client *client, vfs_cmd_t *vc)
     70 {
     71 	struct ofile *ofile;
     72 	vfs_res_t *res;
     73 
     74 	res = malloc (sizeof (vfs_res_t));
     75 	ofile = client->ioctx.fdarray.ofile[vc->data[0]];
     76 	shm_write (ofile, vc->data[1], 0, ofile->length, &res->data[0],
     77 		&res->data[1]);
     78 	res->status = VFS_OK;
     79 	res->errno = 0;
     80 	return res;
     81 }
     82 
     83 int vfs_mkdir (const char *dir, mode_t mode)
     84 {
     85 	const char *s;
     86 	int res;
     87 	struct superblock *super;
     88 	struct vnode *vnode;
     89 
     90 #ifdef VFS_DEBUG
     91 	printf ("vfs_mkdir %s %d\n", dir, mode);
     92 #endif
     93 
     94 	/* find superblock and check if mkdir supported */
     95 	super = fs_find (dir);
     96 	if (super->sb_vops->mkdir == NULL)
     97 		return ENOSYS;
     98 
     99 	/* XXX dispatch to root vnode for now */
    100 	vnode = super->sb_root;
    101 	s = dir + strlen (super->sb_dir);
    102 	res = super->sb_vops->mkdir (vnode, s, mode);
    103 	return res;
    104 }
    105 
    106 vfs_res_t *vfs_opendir (struct client *client, vfs_cmd_t *vc)
    107 {
    108 	char *reldir, *dir;
    109 	int i;
    110 	vfs_res_t *res;
    111 	struct ofile *ofile;
    112 	struct superblock *super;
    113 	struct vnode *vnode;
    114 
    115 	reldir = client->nameptr + vc->data[0];
    116 	res = malloc (sizeof (vfs_res_t));
    117 #ifdef VFS_DEBUG
    118 	printf ("vfs_opendir %s\n", reldir);
    119 #endif
    120 
    121 	/* get a file descriptor */
    122 	for (i = 0; i < MAX_FDS; i++)
    123 		if (client->ioctx.fdarray.ofile[i] == NULL)
    124 			break;
    125 	if (i == MAX_FDS)
    126 	{
    127 		res->status = VFS_ERROR;
    128 		res->errno = EMFILE;
    129 		return res;
    130 	}
    131 
    132 	/* is opendir supported? */
    133 	dir = malloc (BLT_MAX_NAME_LENGTH);
    134 	strlcpy (dir, client->ioctx.cwd, BLT_MAX_NAME_LENGTH);
    135 	path_combine (client->ioctx.cwd, reldir, dir);
    136 	super = fs_find (dir);
    137 	if (super == NULL)
    138 	{
    139 		res->status = VFS_ERROR;
    140 		res->errno = ENOENT;
    141 		free (dir);
    142 		return res;
    143 	}
    144 	if (super->sb_vops->opendir == NULL)
    145 	{
    146 		res->status = VFS_ERROR;
    147 		res->errno = ENOSYS;
    148 		free (dir);
    149 		return res;
    150 	}
    151 
    152 	/* find directory's vnode */
    153 	if (!strcmp (dir, super->sb_dir))
    154 		vnode = super->sb_root;
    155 	else
    156 		vnode = super->sb_vops->walk (super->sb_root, dir +
    157 			strlen (super->sb_dir) + 1);
    158 	if (vnode == NULL)
    159 	{
    160 		res->status = VFS_ERROR;
    161 		res->errno = ENOENT;
    162 		free (dir);
    163 		return res;
    164 	}
    165 
    166 	/* stat here to check for directory */
    167 
    168 	/* call filesystem's opendir and read the directory */
    169 	ofile = client->ioctx.fdarray.ofile[i] = malloc (sizeof (struct ofile));
    170 	ofile->o_vnode = vnode;
    171 	ofile->area = area_clone (vc->data[1], 0, &ofile->dataptr, 0);
    172 	ofile->offset = vc->data[2];
    173 	ofile->length = vc->data[3];
    174 	res->errno = super->sb_vops->opendir (vnode, &ofile->o_cookie);
    175 	res->status = res->errno ? VFS_ERROR : VFS_OK;
    176 	res->data[0] = i;
    177 	free (dir);
    178 	return res;
    179 }
    180 
    181 vfs_res_t *vfs_closedir (struct client *client, vfs_cmd_t *vc)
    182 {
    183 	vfs_res_t *res;
    184 	struct ofile *ofile;
    185 	struct vnode *vnode;
    186 
    187 #ifdef VFS_DEBUG
    188 	printf ("vfs_closedir %d\n", vc->data[0]);
    189 #endif
    190 	res = malloc (sizeof (vfs_res_t));
    191 
    192 	/* is file descriptor valid? */
    193 	if ((ofile = client->ioctx.fdarray.ofile[vc->data[0]]) == NULL)
    194 	{
    195 		res->errno = EBADF;
    196 		res->status = VFS_ERROR;
    197 		return res;
    198 	}
    199 
    200 	/* is closedir supported? */
    201 	vnode = ofile->o_vnode;
    202 	if (vnode->v_sb->sb_vops->closedir == NULL)
    203 	{
    204 		res->errno = ENOSYS;
    205 		res->status = VFS_ERROR;
    206 		return res;
    207 	}
    208 
    209 	/* call filesystem */
    210 	vnode->v_sb->sb_vops->closedir (vnode, ofile->o_cookie);
    211 	if (!res->errno && (vnode->v_sb->sb_vops->free_dircookie != NULL))
    212 		vnode->v_sb->sb_vops->free_dircookie (ofile->o_cookie);
    213 	free (ofile);
    214 	client->ioctx.fdarray.ofile[vc->data[0]] = NULL;
    215 
    216 	res->status = VFS_OK;
    217 	res->errno = 0;
    218 	return res;
    219 }
    220 
    221 vfs_res_t *vfs_open (struct client *client, vfs_cmd_t *vc)
    222 {
    223 	int i;
    224 	char *path;
    225 	vfs_res_t *res;
    226 	struct ofile *ofile;
    227 	struct superblock *super;
    228 	struct vnode *vnode;
    229 
    230 	res = malloc (sizeof (vfs_res_t));
    231 	path = client->nameptr + vc->data[0];
    232 #ifdef VFS_DEBUG
    233 	printf ("vfs_open %s\n", path);
    234 #endif
    235 
    236 	/* get a file descriptor */
    237 	for (i = 0; i < MAX_FDS; i++)
    238 		if (client->ioctx.fdarray.ofile[i] == NULL)
    239 			break;
    240 	if (i == MAX_FDS)
    241 	{
    242 		res->status = VFS_ERROR;
    243 		res->errno = EMFILE;
    244 		return res;
    245 	}
    246 
    247 	/* is open supported? */
    248 	super = fs_find (path);
    249 	if (super == NULL)
    250 	{
    251 		res->status = VFS_ERROR;
    252 		res->errno = ENOENT;
    253 		return res;
    254 	}
    255 	if (super->sb_vops->open == NULL)
    256 	{
    257 		res->status = VFS_ERROR;
    258 		res->errno = ENOSYS;
    259 		return res;
    260 	}
    261 
    262 	/* find file's vnode */
    263 	vnode = super->sb_vops->walk (super->sb_root, path +
    264 		strlen (super->sb_dir) + 1);
    265 	if (vnode == NULL)
    266 	{
    267 		res->status = VFS_ERROR;
    268 		res->errno = ENOENT;
    269 		return res;
    270 	}
    271 
    272 	/* call filesystem */
    273 	ofile = client->ioctx.fdarray.ofile[i] = malloc (sizeof (struct ofile));
    274 	ofile->o_vnode = vnode;
    275 	ofile->o_pos = 0;
    276 	ofile->area = area_clone (vc->data[1], 0, &ofile->dataptr, 0);
    277 	ofile->offset = vc->data[2];
    278 	ofile->length = vc->data[3];
    279 	res->errno = super->sb_vops->open (vnode, &ofile->o_cookie);
    280 	res->status = res->errno ? VFS_ERROR : VFS_OK;
    281 	res->data[0] = i;
    282 	return res;
    283 }
    284 
    285 vfs_res_t *vfs_close (struct client *client, vfs_cmd_t *vc)
    286 {
    287 	vfs_res_t *res;
    288 	struct ofile *ofile;
    289 	struct superblock *super;
    290 
    291 	res = malloc (sizeof (vfs_res_t));
    292 #ifdef VFS_DEBUG
    293 	printf ("vfs_close\n");
    294 #endif
    295 
    296 	ofile = client->ioctx.fdarray.ofile[vc->data[0]];
    297 	if (ofile == NULL)
    298 	{
    299 		res->status = VFS_ERROR;
    300 		res->errno = EBADF;
    301 		return res;
    302 	}
    303 	super = ofile->o_vnode->v_sb;
    304 	if (super->sb_vops->close == NULL)
    305 	{
    306 		res->status = VFS_ERROR;
    307 		res->errno = ENOSYS;
    308 		return res;
    309 	}
    310 	res->errno = super->sb_vops->close (ofile->o_vnode,
    311 		ofile->o_cookie);
    312 
    313 	if (super->sb_vops->free_cookie != NULL)
    314 		super->sb_vops->free_cookie (ofile->o_cookie);
    315 
    316 	res->status = res->errno ? VFS_ERROR : VFS_OK;
    317 	return res;
    318 }
    319 
    320 vfs_res_t *vfs_read (struct client *client, vfs_cmd_t *vc, void **data,
    321 	int *len)
    322 {
    323 	size_t numread;
    324 	void *buf;
    325 	vfs_res_t *res;
    326 	struct ofile *ofile;
    327 	struct superblock *super;
    328 
    329 #ifdef VFS_DEBUG
    330 	printf ("vfs_read\n");
    331 #endif
    332 	res = malloc (sizeof (vfs_res_t));
    333 	buf = malloc (vc->data[1]);
    334 
    335 	ofile = client->ioctx.fdarray.ofile[vc->data[0]];
    336 	if (ofile == NULL)
    337 	{
    338 		res->status = VFS_ERROR;
    339 		res->errno = EBADF;
    340 		return res;
    341 	}
    342 	super = ofile->o_vnode->v_sb;
    343 	if (super->sb_vops->read == NULL)
    344 	{
    345 		res->status = VFS_ERROR;
    346 		res->errno = ENOSYS;
    347 		return res;
    348 	}
    349 
    350 	res->errno = super->sb_vops->read (ofile->o_vnode, buf, vc->data[1],
    351 		ofile->o_pos, &numread, ofile->o_cookie);
    352 	res->status = res->errno ? VFS_ERROR : VFS_OK;
    353 	res->data[0] = numread;
    354 	ofile->o_pos += *len = numread;
    355 	*data = buf;
    356 	return res;
    357 }
    358 
    359 vfs_res_t *vfs_rstat (struct client *client, vfs_cmd_t *vc)
    360 {
    361 	char *path;
    362 	vfs_res_t *res;
    363 	struct superblock *super;
    364 	struct vnode *vnode;
    365 	struct stat *buf;
    366 
    367 #ifdef VFS_DEBUG
    368 	printf ("vfs_rstat\n");
    369 #endif
    370 	res = malloc (sizeof (vfs_res_t) + sizeof (struct stat));
    371 	buf = (void *) res + sizeof (vfs_res_t);
    372 	path = client->nameptr + vc->data[0];
    373 
    374 	/* is stat supported? */
    375 	super = fs_find (path);
    376 	if (super == NULL)
    377 	{
    378 		res->status = VFS_ERROR;
    379 		res->errno = ENOENT;
    380 		return res;
    381 	}
    382 	if (super->sb_vops->rstat == NULL)
    383 	{
    384 		res->status = VFS_ERROR;
    385 		res->errno = ENOSYS;
    386 		return res;
    387 	}
    388 
    389 	/* find file's vnode */
    390 	vnode = super->sb_vops->walk (super->sb_root, path +
    391 		strlen (super->sb_dir) + 1);
    392 	if (vnode == NULL)
    393 	{
    394 		res->status = VFS_ERROR;
    395 		res->errno = ENOENT;
    396 		return res;
    397 	}
    398 
    399 	/* call filesystem */
    400 	res->errno = super->sb_vops->rstat (vnode, buf);
    401 	res->status = res->errno ? VFS_ERROR : VFS_OK;
    402 	return res;
    403 }
    404 
    405 void vfs_tell_cmd (const char *cmd, char *arg)
    406 {
    407 	char *name;
    408 	const char *type, *dir, *data;
    409 	int i, len;
    410 	void *handle;
    411 
    412 	if (!strcmp (cmd, "load"))
    413 	{
    414 		printf (" %s", arg);
    415         name = malloc (len = BLT_MAX_NAME_LENGTH);
    416         strlcpy (name, "/boot/", len);
    417         strlcat (name, arg, len);
    418         strlcat (name, ".so", len);
    419         handle = dlopen (name, 0);
    420         if (handle == NULL)
    421         {
    422             printf ("(error)");
    423             return;
    424         }
    425         free (name);
    426 	}
    427 	else if (!strcmp (cmd, "mount"))
    428 	{
    429 		type = arg;
    430 		for (i = 0; arg[i] != ' '; i++) ;
    431 		arg[i] = 0;
    432 		dir = arg + i + 1;
    433 		for (arg++; arg[i] != ' '; i++) ;
    434 		arg[i] = 0;
    435 		data = arg + i + 1;
    436 		vfs_mount (dir, type, 0, data);
    437 	}
    438 }
    439 
    440 void vfs_tell_parse (const char *msg)
    441 {
    442     const char *c, *cmd;
    443     char *full;
    444     char *d;
    445     int i, whole, len;
    446 
    447     c = msg;
    448     cmd = full = NULL;
    449     whole = len = 0;
    450     while (*c)
    451     {
    452         i = 0;
    453         while ((c[i] != ' ') && c[i])
    454             i++;
    455         d = malloc (i + 1);
    456         strlcpy (d, c, i + 1);
    457         if (cmd == NULL)
    458         {
    459             cmd = d;
    460             if (!strcmp (cmd, "load"))
    461                 printf ("vfs: loading drivers.  [");
    462             else if (!strcmp (cmd, "mount"))
    463             {
    464                 whole = 1;
    465                 full = malloc (len = strlen (msg + 1));
    466                 *full = 0;
    467             }
    468         }
    469         else if (!whole)
    470             vfs_tell_cmd (cmd, d);
    471         else
    472         {
    473             strlcat (full, d, len);
    474             strlcat (full, " ", len);
    475         }
    476         if (i != strlen (c))
    477             c += i + 1;
    478         else
    479             break;
    480     }
    481 	if (whole)
    482 		full[strlen (full) - 1] = 0;
    483     if (!strcmp (cmd, "load"))
    484         printf (" ]\n");
    485 	else if (!strcmp (cmd, "mount"))
    486 		vfs_tell_cmd (cmd, full);
    487 }
    488 
    489 void vfs_tell (void)
    490 {
    491 	char buf[64];
    492 	int port;
    493 	msg_hdr_t mh;
    494 
    495 	__libc_init_fdl ();
    496 	__libc_init_vfs ();
    497 	__dlinit ();
    498 	__blk_ref++;
    499 
    500 	port = port_create (0, "vfs:tell");
    501 	namer_register (port, "vfs:tell");
    502 
    503 	for (;;)
    504 	{
    505 		mh.src = 0;
    506 		mh.dst = port;
    507 		mh.data = buf;
    508 		mh.size = sizeof (buf);
    509 		old_port_recv (&mh);
    510 		vfs_tell_parse (mh.data);
    511 
    512 		mh.dst = mh.src;
    513 		mh.src = port;
    514 		mh.data = &port;
    515 		mh.size = 1;
    516 		old_port_send (&mh);
    517 	}
    518 }
    519 
    520 int vfs_main (volatile int *ready)
    521 {
    522 	int i, size, len;
    523 	void *data;
    524 	msg_hdr_t msg, reply;
    525 	vfs_cmd_t vc;
    526 	vfs_res_t *res;
    527 	struct client *client;
    528 
    529 	/* open a connection to the console */
    530 	__libc_init_console ();
    531 
    532 	/* get a public port and register ourself with the namer */
    533 	vfs_port = port_create (0, "vfs_listen_port");
    534 	namer_register (vfs_port, "vfs");
    535 
    536 	/* say hello */
    537 #ifdef VFS_DEBUG
    538 	printf ("vfs: " FG_GREEN "listener ready" FG_WHITE " (port %d)\n",
    539 		vfs_port);
    540 #endif
    541 
    542 	/* initialise structures */
    543 	fs_drivers = NULL;
    544 	conn_table = hashtable_new (0.75);
    545 	res = NULL;
    546 	client = NULL;
    547 
    548 	/* mount the root and boot filesystems */
    549 	fs_register (&rootfs);
    550 	fs_register (&bootfs);
    551 	vfs_mount ("/", "rootfs", 0, NULL);
    552 	vfs_mkdir ("/boot", 755);
    553 	vfs_mount ("/boot", "bootfs", 0, NULL);
    554 	thr_create (vfs_tell, 0, "vfs:tell");
    555 	*ready = 1;
    556 
    557 	for (;;)
    558 	{
    559 		/*
    560 		 * listen for commands.  all ports will be slaved to the one
    561 		 * we just created, so we only need to listen on one port.
    562 		 */
    563 		msg.src = 0;
    564 		msg.dst = vfs_port;
    565 		msg.data = &vc;
    566 		msg.size = sizeof (vc);
    567 		old_port_recv (&msg);
    568 
    569 		if (vc.cmd != VFS_OPENCONN)
    570 			client = hashtable_lookup (conn_table, msg.src, NULL);
    571 		size = sizeof (vfs_res_t);
    572 		data = NULL;
    573 
    574 		switch (vc.cmd)
    575 		{
    576 			case VFS_OPENCONN:
    577 				res = vfs_openconn (msg.src, vc.data[0]);
    578 				break;
    579 
    580 			case VFS_SCROLL_AREA:
    581 				res = vfs_scroll_area (client, &vc);
    582 				break;
    583 
    584 			case VFS_OPENDIR:
    585 				res = vfs_opendir (client, &vc);
    586 				if (res->status == VFS_OK)
    587 					shm_write_dir (client->ioctx.fdarray.ofile[res->data[0]],
    588 						0, 0, vc.data[3], &res->data[1], &res->data[2]);
    589 				break;
    590 
    591 			case VFS_CLOSEDIR:
    592 				res = vfs_closedir (client, &vc);
    593 				break;
    594 
    595 			case VFS_OPEN:
    596 				res = vfs_open (client, &vc);
    597 				if (res->status == VFS_OK)
    598 					shm_write (client->ioctx.fdarray.ofile[res->data[0]],
    599 						0, 0, vc.data[3], &res->data[1], &res->data[2]);
    600 				break;
    601 
    602 			case VFS_CLOSE:
    603 				res = vfs_close (client, &vc);
    604 				break;
    605 
    606 			case VFS_READ:
    607 				res = vfs_read (client, &vc, &data, &len);
    608 				break;
    609 
    610 			case VFS_RSTAT:
    611 				res = vfs_rstat (client, &vc);
    612 				size = sizeof (vfs_res_t) + sizeof (struct stat);
    613 				break;
    614 
    615 			case VFS_MKDIR:
    616 				res = malloc (sizeof (vfs_res_t));
    617 				res->status = vfs_mkdir (client->nameptr + vc.data[0],
    618 					vc.data[1]);
    619 				res->errno = 0;
    620 				break;
    621 		}
    622 
    623 		if (res != NULL)
    624 		{
    625 			reply.src = (vc.cmd == VFS_OPENCONN) ? vfs_port : client->out;
    626 			reply.dst = msg.src;
    627 			reply.data = res;
    628 			reply.size = size;
    629 			old_port_send (&reply);
    630 			free (res);
    631 
    632 			if (data != NULL)
    633 			{
    634 				reply.src = client->out;
    635 				reply.dst = msg.src;
    636 				if (len < 0x1000)
    637 				{
    638 					reply.data = data;
    639 					reply.size = len;
    640 					old_port_send (&reply);
    641 				}
    642 				else
    643 				{
    644 					for (i = 0; len > 0x1000; i += 0x1000, len -= 0x1000)
    645 					{
    646 						reply.data = (char *) data + i;
    647 						reply.size = 0x1000;
    648 						old_port_send (&reply);
    649 					}
    650 					reply.data = (char *) data + i;
    651 					reply.size = len;
    652 					old_port_send (&reply);
    653 				}
    654 				free (data);
    655 			}
    656 		}
    657 	}
    658 
    659 	/* not reached */
    660 	return 0;
    661 }
    662 
    663 int main (void)
    664 {
    665 	volatile int ready = 0;
    666 
    667 	thr_create (vfs_main, (int *) &ready, "vfs");
    668 	while (!ready) ;
    669 	return 0;
    670 }
    671