Home | History | Annotate | Line # | Download | only in nbfs
      1 /* $NetBSD: nbfs.c,v 1.11 2014/03/21 16:43:00 christos Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2006 Ben Harris
      5  * Copyright (c) 1993
      6  *	The Regents of the University of California.  All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  * 1. Redistributions of source code must retain the above copyright
     12  *    notice, this list of conditions and the following disclaimer.
     13  * 2. Redistributions in binary form must reproduce the above copyright
     14  *    notice, this list of conditions and the following disclaimer in the
     15  *    documentation and/or other materials provided with the distribution.
     16  * 3. Neither the name of the University nor the names of its contributors
     17  *    may be used to endorse or promote products derived from this software
     18  *    without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     30  * SUCH DAMAGE.
     31  */
     32 
     33 /*
     34  * Copyright (c) 1996
     35  *	Matthias Drochner.  All rights reserved.
     36  *
     37  * Redistribution and use in source and binary forms, with or without
     38  * modification, are permitted provided that the following conditions
     39  * are met:
     40  * 1. Redistributions of source code must retain the above copyright
     41  *    notice, this list of conditions and the following disclaimer.
     42  * 2. Redistributions in binary form must reproduce the above copyright
     43  *    notice, this list of conditions and the following disclaimer in the
     44  *    documentation and/or other materials provided with the distribution.
     45  *
     46  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     47  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     48  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     49  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     50  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     51  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     52  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     53  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     54  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     55  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     56  */
     57 
     58 #include <sys/types.h>
     59 #include <sys/param.h>
     60 #include <sys/disklabel.h>
     61 #include <sys/queue.h>
     62 #include <ufs/ufs/dinode.h>
     63 #include <ufs/ufs/dir.h>
     64 #include <lib/libkern/libkern.h>
     65 #include <lib/libsa/stand.h>
     66 #include <lib/libsa/lfs.h>
     67 #include <lib/libsa/ufs.h>
     68 #include <riscoscalls.h>
     69 #include <riscosdisk.h>
     70 
     71 #include "nbfs.h"
     72 
     73 struct fs_ops file_system[] = {
     74 	FS_OPS(ffsv1), FS_OPS(ffsv2), FS_OPS(lfsv1), FS_OPS(lfsv2)
     75 };
     76 
     77 int nfsys = __arraycount(file_system);
     78 
     79 struct nbfs_open_file {
     80 	struct	open_file f;
     81 	int	fileswitch_handle;
     82 	LIST_ENTRY(nbfs_open_file) link;
     83 };
     84 
     85 static LIST_HEAD(, nbfs_open_file) nbfs_open_files;
     86 
     87 static os_error const *maperr(int saerr);
     88 
     89 /*
     90  * Given a RISC OS special field and pathname, open the relevant
     91  * device and return a pointer to the remainder of the pathname.
     92  */
     93 static int
     94 nbfs_devopen(struct open_file *f, char const *special, char const *fname,
     95     char const **rest)
     96 {
     97 	unsigned int drive = 0, part = RAW_PART;
     98 	int err;
     99 
    100 	if (*fname++ != ':')
    101 		return EINVAL;
    102 	while (isdigit((unsigned char)*fname))
    103 		drive = drive * 10 + *fname++ - '0';
    104 	if (islower((unsigned char)*fname))
    105 		part = *fname++ - 'a';
    106 	else if (isupper((unsigned char)*fname))
    107 		part = *fname++ - 'A';
    108 	if (*fname != '.' && *fname != '\0')
    109 		return EINVAL;
    110 	err = rodisk_open(f, special, drive, part);
    111 	if (err != 0)
    112 		return err;
    113 	*rest = fname;
    114 	if (**rest == '.')
    115 		(*rest)++;
    116 	return 0;
    117 }
    118 
    119 static int
    120 nbfs_fileopen(struct open_file *f, char const *tail)
    121 {
    122 	char *file, *p;
    123 	int i, error = ENOENT;
    124 
    125 	if (tail[0] == '$' && tail[1] == '.')
    126 		tail += 2;
    127 	file = alloc(strlen(tail) + 2);
    128 	strcpy(file, "/");
    129 	strcat(file, tail);
    130 	for (p = file + 1; *p != '\0'; p++) {
    131 		if (*p == '.')
    132 			*p = '/';
    133 		else if (*p == '/')
    134 			*p = '.';
    135 	}
    136 	if (strcmp(tail, "$") == 0)
    137 		strcpy(file, "/");
    138 
    139 	for (i = 0; i < nfsys; i++) {
    140 		error = FS_OPEN(&file_system[i])(file, f);
    141 		if (error == 0 || error == ENOENT) {
    142 			f->f_ops = &file_system[i];
    143 			break;
    144 		}
    145 	}
    146 	dealloc(file, strlen(file) + 1);
    147 	return error;
    148 }
    149 
    150 static int
    151 nbfs_fopen(struct open_file *f, char const *special, char const *path)
    152 {
    153 	char const *tail;
    154 	int err;
    155 
    156 	err = nbfs_devopen(f, special, path, &tail);
    157 	if (err != 0)
    158 		return err;
    159 	err = nbfs_fileopen(f, tail);
    160 	if (err != 0)
    161 		DEV_CLOSE(f->f_dev)(f);
    162 	return err;
    163 }
    164 
    165 static int
    166 nbfs_fclose(struct open_file *f)
    167 {
    168 	int ferr, derr;
    169 
    170 	ferr = FS_CLOSE(f->f_ops)(f);
    171 	derr = DEV_CLOSE(f->f_dev)(f);
    172 	return ferr != 0 ? ferr : derr;
    173 }
    174 
    175 os_error const *
    176 nbfs_open(struct nbfs_reg *r)
    177 {
    178 	int reason = r->r0;
    179 	char const *fname = (char const *)r->r1;
    180 	int fh = r->r3;
    181 	char const *special = (char const *)r->r6;
    182 	int err;
    183 	struct nbfs_open_file *nof = NULL;
    184 	struct stat st;
    185 
    186 	switch (reason) {
    187 	case 0: /* Open for read */
    188 	case 1: /* Create and open for update */
    189 	case 2: /* Open for update */
    190 		nof = alloc(sizeof(*nof));
    191 		memset(nof, 0, sizeof(*nof));
    192 		err = nbfs_fopen(&nof->f, special, fname);
    193 		if (err != 0)
    194 			goto fail;
    195 		err = FS_STAT(nof->f.f_ops)(&nof->f, &st);
    196 		if (err != 0)
    197 			goto fail;
    198 		nof->fileswitch_handle = fh;
    199 		LIST_INSERT_HEAD(&nbfs_open_files, nof, link);
    200 		r->r0 = 0x40000000;
    201 		if (S_ISDIR(st.st_mode))
    202 			r->r0 |= 0x20000000;
    203 		r->r1 = (uint32_t)nof;
    204 		r->r2 = DEV_BSIZE;
    205 		r->r3 = st.st_size;
    206 		r->r4 = st.st_size;
    207 		return NULL;
    208 	default:
    209 		err = EINVAL;
    210 		goto fail;
    211 	}
    212 fail:
    213 	if (nof != NULL)
    214 		dealloc(nof, sizeof(*nof));
    215 	return maperr(err);
    216 }
    217 
    218 os_error const *
    219 nbfs_getbytes(struct nbfs_reg *r)
    220 {
    221 	struct nbfs_open_file *nof = (struct nbfs_open_file *)r->r1;
    222 	void *buf = (void *)r->r2;
    223 	size_t size = r->r3;
    224 	off_t off = r->r4;
    225 	int err;
    226 
    227 	err = FS_SEEK(nof->f.f_ops)(&nof->f, off, SEEK_SET);
    228 	if (err == -1)
    229 		return maperr(err);
    230 	err = FS_READ(nof->f.f_ops)(&nof->f, buf, size, NULL);
    231 	if (err != 0)
    232 		return maperr(err);
    233 	return NULL;
    234 }
    235 
    236 os_error const *
    237 nbfs_putbytes(struct nbfs_reg *r)
    238 {
    239 	static os_error const err = {0, "nbfs_putbytes"};
    240 
    241 	return &err;
    242 }
    243 
    244 os_error const *
    245 nbfs_args(struct nbfs_reg *r)
    246 {
    247 	static os_error const err = {0, "nbfs_args"};
    248 
    249 	return &err;
    250 }
    251 
    252 os_error const *
    253 nbfs_close(struct nbfs_reg *r)
    254 {
    255 	struct nbfs_open_file *nof = (struct nbfs_open_file *)r->r1;
    256 	/* uint32_t loadaddr = r->r2; */
    257 	/* uint32_t execaddr = r->r3; */
    258 	int err;
    259 
    260 	err = nbfs_fclose(&nof->f);
    261 	if (err != 0)
    262 		return maperr(err);
    263 	LIST_REMOVE(nof, link);
    264 	dealloc(nof, sizeof(*nof));
    265 	return NULL;
    266 }
    267 
    268 os_error const *
    269 nbfs_file(struct nbfs_reg *r)
    270 {
    271 	int reason = r->r0;
    272 	char const *fname = (char const *)r->r1;
    273 	void *buf = (void *)r->r2;
    274 	char const *special = (char const *)r->r6;
    275 	struct open_file f;
    276 	int err;
    277 	struct stat st;
    278 
    279 	memset(&f, 0, sizeof(f));
    280 	err = nbfs_fopen(&f, special, fname);
    281 	if (err != 0 && err != ENOENT)
    282 		return maperr(err);
    283 	switch (reason) {
    284 	case 0: /* Save file */
    285 	case 1: /* Write catalogue information */
    286 	case 2: /* Write load address */
    287 	case 3: /* Write execution address */
    288 	case 4: /* Write attributes */
    289 	case 6: /* Delete object */
    290 	case 7: /* Create file */
    291 	case 8: /* Create directory */
    292 		nbfs_fclose(&f);
    293 		err = EROFS;
    294 		goto fail;
    295 	case 5: /* Read catalogue information */
    296 	case 255: /* Load file */
    297 		if (err == ENOENT)
    298 			r->r0 = r->r2 = r->r3 = r->r4 = r->r5 = 0;
    299 		else {
    300 			err = FS_STAT(f.f_ops)(&f, &st);
    301 			if (err != 0)
    302 				goto fail;
    303 			r->r0 = S_ISDIR(st.st_mode) ?
    304 			    fileswitch_IS_DIR : fileswitch_IS_FILE;
    305 			r->r2 = r->r3 = 0;
    306 			r->r4 = st.st_size;
    307 			r->r5 = fileswitch_ATTR_OWNER_READ |
    308 			    fileswitch_ATTR_WORLD_READ;
    309 			if (reason == 255) {
    310 				err = FS_READ(f.f_ops)
    311 				    (&f, buf, st.st_size, NULL);
    312 				if (err != 0)
    313 					goto fail;
    314 				/* R6 should really be the leaf name */
    315 				r->r6 = r->r1;
    316 			}
    317 		}
    318 		break;
    319 	default:
    320 		nbfs_fclose(&f);
    321 		err = EINVAL;
    322 		goto fail;
    323 	}
    324 	nbfs_fclose(&f);
    325 	return NULL;
    326 fail:
    327 	nbfs_fclose(&f);
    328 	return maperr(err);
    329 }
    330 
    331 static int
    332 nbfs_filename_ok(char const *f)
    333 {
    334 
    335 	while (*f)
    336 		if (strchr(":*#$&@^%\\", *f++) != NULL)
    337 			return 0;
    338 	return 1;
    339 }
    340 
    341 static os_error const *
    342 nbfs_func_dirents(struct nbfs_reg *r)
    343 {
    344 	int reason = r->r0;
    345 	char const *fname = (char const *)r->r1;
    346 	char const *special = (char const *)r->r6;
    347 	struct open_file f;
    348 	struct stat st;
    349 	int err;
    350 	struct fileswitch_dirent *fdp;
    351 	size_t resid;
    352 	size_t maxcount = r->r3;
    353 	size_t count = 0;
    354 	size_t skip = r->r4;
    355 	ssize_t off = 0;
    356 	size_t buflen = r->r5;
    357 	char dirbuf[UFS_DIRBLKSIZ];
    358 	char *outp = (char *)r->r2;
    359 
    360 	err = nbfs_fopen(&f, special, fname);
    361 	if (err != 0)
    362 		return maperr(err);
    363 	err = FS_STAT(f.f_ops)(&f, &st);
    364 	if (err != 0)
    365 		goto fail;
    366 	if (!S_ISDIR(st.st_mode)) {
    367 		err = ENOTDIR;
    368 		goto fail;
    369 	}
    370 	while (FS_READ(f.f_ops)(&f, dirbuf, UFS_DIRBLKSIZ, &resid) == 0 &&
    371 	    resid == 0) {
    372 		struct direct  *dp, *edp;
    373 
    374 		dp = (struct direct *) dirbuf;
    375 		edp = (struct direct *) (dirbuf + UFS_DIRBLKSIZ);
    376 
    377 		for (; dp < edp; dp = (void *)((char *)dp + dp->d_reclen)) {
    378 			size_t entsiz = 0;
    379 			int i;
    380 
    381 			if (dp->d_ino ==  0)
    382 				continue;
    383 			/*
    384 			 * Skip ., .., and names with characters that RISC
    385 			 * OS doesn't allow.
    386 			 */
    387 			if (strcmp(dp->d_name, ".") == 0 ||
    388 			    strcmp(dp->d_name, "..") == 0 ||
    389 			    !nbfs_filename_ok(dp->d_name))
    390 				continue;
    391 			if (off++ < skip)
    392 				continue;
    393 
    394 			switch (reason) {
    395 			case 14:
    396 				entsiz = strlen(dp->d_name) + 1;
    397 				if (buflen < entsiz)
    398 					goto out;
    399 				strcpy(outp, dp->d_name);
    400 				break;
    401 			case 15:
    402 				entsiz = ALIGN(offsetof(
    403 					  struct fileswitch_dirent, name)
    404 				    + strlen(dp->d_name) + 1);
    405 				if (buflen < entsiz)
    406 					goto out;
    407 
    408 				fdp = (struct fileswitch_dirent *)outp;
    409 				fdp->loadaddr = 0;
    410 				fdp->execaddr = 0;
    411 				fdp->length = 0;
    412 				fdp->attr = 0;
    413 				fdp->objtype = dp->d_type == DT_DIR ?
    414 				    fileswitch_IS_DIR : fileswitch_IS_FILE;
    415 				strcpy(fdp->name, dp->d_name);
    416 				for (i = 0; fdp->name[i] != '\0'; i++)
    417 					if (fdp->name[i] == '.')
    418 						fdp->name[i] = '/';
    419 				break;
    420 			}
    421 			outp += entsiz;
    422 			buflen -= entsiz;
    423 			if (++count == maxcount)
    424 				goto out;
    425 		}
    426 	}
    427 	off = -1;
    428 out:
    429 	nbfs_fclose(&f);
    430 	r->r3 = count;
    431 	r->r4 = off;
    432 	return NULL;
    433 fail:
    434 	nbfs_fclose(&f);
    435 	return maperr(err);
    436 }
    437 
    438 os_error const *
    439 nbfs_func(struct nbfs_reg *r)
    440 {
    441 	static os_error error = {0, "nbfs_func"};
    442 	int reason = r->r0;
    443 
    444 	switch (reason) {
    445 	case 14:
    446 	case 15:
    447 		return nbfs_func_dirents(r);
    448 
    449 	case 16: /* Shut down */
    450 		return NULL;
    451 	default:
    452 		snprintf(error.errmess, sizeof(error.errmess),
    453 		    "nbfs_func %d not implemented", reason);
    454 		return &error;
    455 	}
    456 }
    457 
    458 #define FSERR(x) (0x10000 | (NBFS_FSNUM << 8) | (x))
    459 
    460 static struct {
    461 	int saerr;
    462 	os_error roerr;
    463 } const errmap[] = {
    464 	{ ECTLR,   { FSERR(ECTLR),   "Bad parent filing system" } },
    465 	{ EUNIT,   { FSERR(0xAC),    "Bad drive number" } },
    466 	{ EPART,   { FSERR(EPART),   "Bad partition" } },
    467 	{ ERDLAB,  { FSERR(ERDLAB),  "Can't read disk label" } },
    468 	{ EUNLAB,  { FSERR(EUNLAB),  "Unlabeled" } },
    469 	{ ENOENT,  { FSERR(0xD6),    "No such file or directory" } },
    470 	{ EIO,     { FSERR(EIO),     "Input/output error" } },
    471 	{ EINVAL,  { FSERR(EINVAL),  "Invalid argument" } },
    472 	{ ENOTDIR, { FSERR(ENOTDIR), "Not a directory" } },
    473 	{ EROFS,   { FSERR(0xC9),    "Read-only file system" } },
    474 };
    475 
    476 static os_error const *maperr(int err)
    477 {
    478 	int i;
    479 	static const os_error defaulterr = { FSERR(0), "Unknown NBFS error" };
    480 
    481 	for (i = 0; i < sizeof(errmap) / sizeof(errmap[0]); i++)
    482 		if (err == errmap[i].saerr)
    483 			return &errmap[i].roerr;
    484 	return &defaulterr;
    485 }
    486