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