xref: /netbsd-src/sys/arch/acorn32/stand/nbfs/nbfs.c (revision a5847cc334d9a7029f6352b847e9e8d71a0f9e0c)
1 /* $NetBSD: nbfs.c,v 1.7 2006/07/13 16:11:41 bjh21 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) return err;
112 	*rest = fname;
113 	if (**rest == '.') (*rest)++;
114 	return 0;
115 }
116 
117 static int
118 nbfs_fileopen(struct open_file *f, char const *tail)
119 {
120 	char *file, *p;
121 	int i, error = ENOENT;
122 
123 	if (tail[0] == '$' && tail[1] == '.')
124 		tail += 2;
125 	file = alloc(strlen(tail) + 2);
126 	strcpy(file, "/");
127 	strcat(file, tail);
128 	for (p = file + 1; *p != '\0'; p++) {
129 		if (*p == '.') *p = '/';
130 		else if (*p == '/') *p = '.';
131 	}
132 	if (strcmp(tail, "$") == 0)
133 		strcpy(file, "/");
134 
135 	for (i = 0; i < nfsys; i++) {
136 		error = FS_OPEN(&file_system[i])(file, f);
137 		if (error == 0 || error == ENOENT) {
138 			f->f_ops = &file_system[i];
139 			break;
140 		}
141 	}
142 	dealloc(file, strlen(file) + 1);
143 	return error;
144 }
145 
146 static int
147 nbfs_fopen(struct open_file *f, char const *special, char const *path)
148 {
149 	char const *tail;
150 	int err;
151 
152 	err = nbfs_devopen(f, special, path, &tail);
153 	if (err != 0) return err;
154 	err = nbfs_fileopen(f, tail);
155 	if (err != 0)
156 		DEV_CLOSE(f->f_dev)(f);
157 	return err;
158 }
159 
160 static int
161 nbfs_fclose(struct open_file *f)
162 {
163 	int ferr, derr;
164 
165 	ferr = FS_CLOSE(f->f_ops)(f);
166 	derr = DEV_CLOSE(f->f_dev)(f);
167 	return ferr != 0 ? ferr : derr;
168 }
169 
170 os_error const *
171 nbfs_open(struct nbfs_reg *r)
172 {
173 	int reason = r->r0;
174 	char const *fname = (char const *)r->r1;
175 	int fh = r->r3;
176 	char const *special = (char const *)r->r6;
177 	int err;
178 	struct nbfs_open_file *nof = NULL;
179 	struct stat st;
180 
181 	switch (reason) {
182 	case 0: /* Open for read */
183 	case 1: /* Create and open for update */
184 	case 2: /* Open for update */
185 		nof = alloc(sizeof(*nof));
186 		memset(nof, 0, sizeof(*nof));
187 		err = nbfs_fopen(&nof->f, special, fname);
188 		if (err != 0) goto fail;
189 		err = FS_STAT(nof->f.f_ops)(&nof->f, &st);
190 		if (err != 0) goto fail;
191 		nof->fileswitch_handle = fh;
192 		LIST_INSERT_HEAD(&nbfs_open_files, nof, link);
193 		r->r0 = 0x40000000;
194 		if (S_ISDIR(st.st_mode)) r->r0 |= 0x20000000;
195 		r->r1 = (uint32_t)nof;
196 		r->r2 = DEV_BSIZE;
197 		r->r3 = st.st_size;
198 		r->r4 = st.st_size;
199 		return NULL;
200 	default:
201 		err = EINVAL;
202 		goto fail;
203 	}
204 fail:
205 	if (nof != NULL)
206 		dealloc(nof, sizeof(*nof));
207 	return maperr(err);
208 }
209 
210 os_error const *
211 nbfs_getbytes(struct nbfs_reg *r)
212 {
213 	struct nbfs_open_file *nof = (struct nbfs_open_file *)r->r1;
214 	void *buf = (void *)r->r2;
215 	size_t size = r->r3;
216 	off_t off = r->r4;
217 	int err;
218 
219 	err = FS_SEEK(nof->f.f_ops)(&nof->f, off, SEEK_SET);
220 	if (err == -1) return maperr(err);
221 	err = FS_READ(nof->f.f_ops)(&nof->f, buf, size, NULL);
222 	if (err != 0) return maperr(err);
223 	return NULL;
224 }
225 
226 os_error const *
227 nbfs_putbytes(struct nbfs_reg *r)
228 {
229 	static os_error const err = {0, "nbfs_putbytes"};
230 
231 	return &err;
232 }
233 
234 os_error const *
235 nbfs_args(struct nbfs_reg *r)
236 {
237 	static os_error const err = {0, "nbfs_args"};
238 
239 	return &err;
240 }
241 
242 os_error const *
243 nbfs_close(struct nbfs_reg *r)
244 {
245 	struct nbfs_open_file *nof = (struct nbfs_open_file *)r->r1;
246 	/* uint32_t loadaddr = r->r2; */
247 	/* uint32_t execaddr = r->r3; */
248 	int err;
249 
250 	err = nbfs_fclose(&nof->f);
251 	if (err != 0) return maperr(err);
252 	LIST_REMOVE(nof, link);
253 	dealloc(nof, sizeof(*nof));
254 	return NULL;
255 }
256 
257 os_error const *
258 nbfs_file(struct nbfs_reg *r)
259 {
260 	int reason = r->r0;
261 	char const *fname = (char const *)r->r1;
262 	void *buf = (void *)r->r2;
263 	char const *special = (char const *)r->r6;
264 	struct open_file f;
265 	int err;
266 	struct stat st;
267 
268 	memset(&f, 0, sizeof(f));
269 	err = nbfs_fopen(&f, special, fname);
270 	if (err != 0 && err != ENOENT)
271 		return maperr(err);
272 	switch (reason) {
273 	case 0: /* Save file */
274 	case 1: /* Write catalogue information */
275 	case 2: /* Write load address */
276 	case 3: /* Write execution address */
277 	case 4: /* Write attributes */
278 	case 6: /* Delete object */
279 	case 7: /* Create file */
280 	case 8: /* Create directory */
281 		nbfs_fclose(&f);
282 		err = EROFS;
283 		goto fail;
284 	case 5: /* Read catalogue information */
285 	case 255: /* Load file */
286 		if (err == ENOENT)
287 			r->r0 = r->r2 = r->r3 = r->r4 = r->r5 = 0;
288 		else {
289 			err = FS_STAT(f.f_ops)(&f, &st);
290 			if (err != 0) goto fail;
291 			r->r0 = S_ISDIR(st.st_mode) ?
292 			    fileswitch_IS_DIR : fileswitch_IS_FILE;
293 			r->r2 = r->r3 = 0;
294 			r->r4 = st.st_size;
295 			r->r5 = fileswitch_ATTR_OWNER_READ |
296 			    fileswitch_ATTR_WORLD_READ;
297 			if (reason == 255) {
298 				err = FS_READ(f.f_ops)
299 				    (&f, buf, st.st_size, NULL);
300 				if (err != 0) goto fail;
301 				/* R6 should really be the leaf name */
302 				r->r6 = r->r1;
303 			}
304 		}
305 		break;
306 	default:
307 		nbfs_fclose(&f);
308 		err = EINVAL;
309 		goto fail;
310 	}
311 	nbfs_fclose(&f);
312 	return NULL;
313 fail:
314 	nbfs_fclose(&f);
315 	return maperr(err);
316 }
317 
318 static int
319 nbfs_filename_ok(char const *f)
320 {
321 
322 	while (*f)
323 		if (strchr(":*#$&@^%\\", *f++) != NULL)
324 			return 0;
325 	return 1;
326 }
327 
328 static os_error const *
329 nbfs_func_dirents(struct nbfs_reg *r)
330 {
331 	int reason = r->r0;
332 	char const *fname = (char const *)r->r1;
333 	char const *special = (char const *)r->r6;
334 	struct open_file f;
335 	struct stat st;
336 	int err;
337 	struct fileswitch_dirent *fdp;
338 	size_t resid;
339 	size_t maxcount = r->r3;
340 	size_t count = 0;
341 	size_t skip = r->r4;
342 	ssize_t off = 0;
343 	size_t buflen = r->r5;
344 	char dirbuf[DIRBLKSIZ];
345 	char *outp = (char *)r->r2;
346 
347 	err = nbfs_fopen(&f, special, fname);
348 	if (err != 0)
349 		return maperr(err);
350 	err = FS_STAT(f.f_ops)(&f, &st);
351 	if (err != 0)
352 		goto fail;
353 	if (!S_ISDIR(st.st_mode)) {
354 		err = ENOTDIR;
355 		goto fail;
356 	}
357 	while (FS_READ(f.f_ops)(&f, dirbuf, DIRBLKSIZ, &resid) == 0 &&
358 	    resid == 0) {
359 		struct direct  *dp, *edp;
360 
361 		dp = (struct direct *) dirbuf;
362 		edp = (struct direct *) (dirbuf + DIRBLKSIZ);
363 
364 		for (; dp < edp; dp = (void *)((char *)dp + dp->d_reclen)) {
365 			size_t entsiz = 0;
366 			int i;
367 
368 			if (dp->d_ino ==  0)
369 				continue;
370 			/*
371 			 * Skip ., .., and names with characters that RISC
372 			 * OS doesn't allow.
373 			 */
374 			if (strcmp(dp->d_name, ".") == 0 ||
375 			    strcmp(dp->d_name, "..") == 0 ||
376 			    !nbfs_filename_ok(dp->d_name))
377 				continue;
378 			if (off++ < skip)
379 				continue;
380 
381 			switch (reason) {
382 			case 14:
383 				entsiz = strlen(dp->d_name) + 1;
384 				if (buflen < entsiz) goto out;
385 				strcpy(outp, dp->d_name);
386 				break;
387 			case 15:
388 				entsiz = ALIGN(offsetof(
389 					  struct fileswitch_dirent, name)
390 				    + strlen(dp->d_name) + 1);
391 				if (buflen < entsiz) goto out;
392 
393 				fdp = (struct fileswitch_dirent *)outp;
394 				fdp->loadaddr = 0;
395 				fdp->execaddr = 0;
396 				fdp->length = 0;
397 				fdp->attr = 0;
398 				fdp->objtype = dp->d_type == DT_DIR ?
399 				    fileswitch_IS_DIR : fileswitch_IS_FILE;
400 				strcpy(fdp->name, dp->d_name);
401 				for (i = 0; fdp->name[i] != '\0'; i++)
402 					if (fdp->name[i] == '.')
403 						fdp->name[i] = '/';
404 				break;
405 			}
406 			outp += entsiz;
407 			buflen -= entsiz;
408 			if (++count == maxcount)
409 				goto out;
410 		}
411 	}
412 	off = -1;
413 out:
414 	nbfs_fclose(&f);
415 	r->r3 = count;
416 	r->r4 = off;
417 	return NULL;
418 fail:
419 	nbfs_fclose(&f);
420 	return maperr(err);
421 }
422 
423 os_error const *
424 nbfs_func(struct nbfs_reg *r)
425 {
426 	static os_error error = {0, "nbfs_func"};
427 	int reason = r->r0;
428 
429 	switch (reason) {
430 	case 14:
431 	case 15:
432 		return nbfs_func_dirents(r);
433 
434 	case 16: /* Shut down */
435 		return NULL;
436 	default:
437 		sprintf(error.errmess, "nbfs_func %d not implemented", reason);
438 		return &error;
439 	}
440 }
441 
442 #define FSERR(x) (0x10000 | (NBFS_FSNUM << 8) | (x))
443 
444 static struct {
445 	int saerr;
446 	os_error roerr;
447 } const errmap[] = {
448 	{ ECTLR,   { FSERR(ECTLR),   "Bad parent filing system" } },
449 	{ EUNIT,   { FSERR(0xAC),    "Bad drive number" } },
450 	{ EPART,   { FSERR(EPART),   "Bad partition" } },
451 	{ ERDLAB,  { FSERR(ERDLAB),  "Can't read disk label" } },
452 	{ EUNLAB,  { FSERR(EUNLAB),  "Unlabeled" } },
453 	{ ENOENT,  { FSERR(0xD6),    "No such file or directory" } },
454 	{ EIO,     { FSERR(EIO),     "Input/output error" } },
455 	{ EINVAL,  { FSERR(EINVAL),  "Invalid argument" } },
456 	{ ENOTDIR, { FSERR(ENOTDIR), "Not a directory" } },
457 	{ EROFS,   { FSERR(0xC9),    "Read-only file system" } },
458 };
459 
460 static os_error const *maperr(int err)
461 {
462 	int i;
463 	static const os_error defaulterr = { FSERR(0), "Unknown NBFS error" };
464 
465 	for (i = 0; i < sizeof(errmap) / sizeof(errmap[0]); i++)
466 		if (err == errmap[i].saerr)
467 			return &errmap[i].roerr;
468 	return &defaulterr;
469 }
470