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
nbfs_devopen(struct open_file * f,char const * special,char const * fname,char const ** rest)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
nbfs_fileopen(struct open_file * f,char const * tail)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
nbfs_fopen(struct open_file * f,char const * special,char const * path)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
nbfs_fclose(struct open_file * f)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 *
nbfs_open(struct nbfs_reg * r)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 *
nbfs_getbytes(struct nbfs_reg * r)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 *
nbfs_putbytes(struct nbfs_reg * r)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 *
nbfs_args(struct nbfs_reg * r)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 *
nbfs_close(struct nbfs_reg * r)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 *
nbfs_file(struct nbfs_reg * r)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
nbfs_filename_ok(char const * f)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 *
nbfs_func_dirents(struct nbfs_reg * r)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 *
nbfs_func(struct nbfs_reg * r)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
maperr(int err)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