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