1 /*
2 * The ARAnyM MetaDOS driver.
3 *
4 * 2002 STan
5 *
6 * Based on:
7 * filesys.c,v 1.24 2002/01/25 09:23:52 fna Exp
8 *
9 * This file has been modified as part of the FreeMiNT project. See
10 * the file Changes.MH for details and dates.
11 *
12 *
13 * Copyright 1990,1991,1992 Eric R. Smith.
14 * Copyright 1992,1993,1994 Atari Corp.
15 * All rights reserved.
16 *
17 */
18
19 /*
20 * various file system interface things
21 */
22
23 #include "hostfs.h"
24 #include "mint/assert.h"
25 #include "mint/string.h"
26 #include "mint/errno.h"
27 #include "mint/ctype.h"
28 #include "mint/credentials.h"
29
30 #if 1
31 #define PATH2COOKIE_DB(x) TRACE(x)
32 #else
33 #define PATH2COOKIE_DB(x) DEBUG(x)
34 #endif
35
36
37 long
getxattr(FILESYS * fs,fcookie * fc,XATTR * xattr)38 getxattr(FILESYS *fs, fcookie *fc, XATTR *xattr)
39 {
40 STAT stat;
41 long r;
42
43 assert(fs->fsflags & FS_EXT_3);
44
45 r = xfs_stat64(fs, fc, &stat);
46 if (!r)
47 {
48 xattr->mode = stat.mode;
49 xattr->index = stat.ino;
50 xattr->dev = stat.dev;
51 xattr->rdev = stat.rdev;
52 xattr->nlink = stat.nlink;
53 xattr->uid = stat.uid;
54 xattr->gid = stat.gid;
55 xattr->size = stat.size;
56 xattr->blksize = stat.blksize;
57 xattr->nblocks = (stat.blksize < 512) ? stat.blocks :
58 stat.blocks / (stat.blksize >> 9);
59
60 SET_XATTR_TD(xattr,m,stat.mtime.time);
61 SET_XATTR_TD(xattr,a,stat.atime.time);
62 SET_XATTR_TD(xattr,c,stat.ctime.time);
63 xattr->attr = 0;
64
65 /* fake attr field a little bit */
66 if (S_ISDIR(stat.mode))
67 xattr->attr = FA_DIR;
68 else if (!(stat.mode & 0222))
69 xattr->attr = FA_RDONLY;
70
71 xattr->reserved2 = 0;
72 xattr->reserved3[0] = 0;
73 xattr->reserved3[1] = 0;
74 }
75
76 return r;
77 }
78
79 long
getstat64(FILESYS * fs,fcookie * fc,STAT * stat)80 getstat64(FILESYS *fs, fcookie *fc, STAT *stat)
81 {
82 XATTR xattr;
83 long r;
84
85 assert(fs->getxattr);
86
87 r = xfs_getxattr(fs, fc, &xattr);
88 if (!r)
89 {
90 stat->dev = xattr.dev;
91 stat->ino = xattr.index;
92 stat->mode = xattr.mode;
93 stat->nlink = xattr.nlink;
94 stat->uid = xattr.uid;
95 stat->gid = xattr.gid;
96 stat->rdev = xattr.rdev;
97
98 /* no native UTC extension
99 * -> convert to unix UTC
100 */
101 stat->atime.high_time = 0;
102 stat->atime.time = unixtime (xattr.atime, xattr.adate) + timezone;
103 stat->atime.nanoseconds = 0;
104
105 stat->mtime.high_time = 0;
106 stat->mtime.time = unixtime (xattr.mtime, xattr.mdate) + timezone;
107 stat->mtime.nanoseconds = 0;
108
109 stat->ctime.high_time = 0;
110 stat->ctime.time = unixtime (xattr.ctime, xattr.cdate) + timezone;
111 stat->ctime.nanoseconds = 0;
112
113 stat->size = xattr.size;
114 stat->blocks = (xattr.blksize < 512) ? xattr.nblocks :
115 xattr.nblocks * (xattr.blksize >> 9);
116 stat->blksize = xattr.blksize;
117
118 stat->flags = 0;
119 stat->gen = 0;
120
121 bzero(stat->res, sizeof(stat->res));
122 }
123
124 return r;
125 }
126
127
128 /*
129 * routines for parsing path names
130 */
131
132 /*
133 * relpath2cookie converts a TOS file name into a file cookie representing
134 * the directory the file resides in, and a character string representing
135 * the name of the file in that directory. The character string is
136 * copied into the "lastname" array. If lastname is NULL, then the cookie
137 * returned actually represents the file, instead of just the directory
138 * the file is in.
139 *
140 * note that lastname, if non-null, should be big enough to contain all the
141 * characters in "path", since if the file system doesn't want the kernel
142 * to do path name parsing we may end up just copying path to lastname
143 * and returning the current or root directory, as appropriate
144 *
145 * "relto" is the directory relative to which the search should start.
146 * if you just want the current directory, use path2cookie instead.
147 *
148 */
149
150 long _cdecl
relpath2cookie(struct proc * p,fcookie * relto,const char * path,char * lastname,fcookie * res,int depth)151 relpath2cookie(struct proc *p, fcookie *relto, const char *path, char *lastname,
152 fcookie *res, int depth)
153 {
154 char newpath[16];
155
156 struct cwd *cwd = p->p_cwd;
157
158 char temp2[PATH_MAX];
159 char linkstuff[PATH_MAX];
160
161 fcookie dir;
162 int drv;
163 XATTR xattr;
164 long r;
165
166
167 /* dolast: 0 == return a cookie for the directory the file is in
168 * 1 == return a cookie for the file itself, don't follow links
169 * 2 == return a cookie for whatever the file points at
170 */
171 int dolast = 0;
172 int i = 0;
173
174 if (!path)
175 return ENOTDIR;
176
177 if (*path == '\0')
178 return ENOENT;
179
180 if (!lastname)
181 {
182 dolast = 1;
183 lastname = temp2;
184 }
185 else if (lastname == follow_links)
186 {
187 dolast = 2;
188 lastname = temp2;
189 }
190
191 *lastname = '\0';
192
193 PATH2COOKIE_DB (("relpath2cookie(%s, dolast=%d, depth=%d [relto %p, %i])",
194 path, dolast, depth, relto->fs, relto->dev));
195
196 if (depth > MAX_LINKS)
197 {
198 DEBUG (("Too many symbolic links"));
199 return ELOOP;
200 }
201
202 /* special cases: CON:, AUX:, etc. should be converted to U:\DEV\CON,
203 * U:\DEV\AUX, etc.
204 */
205 # if 1
206 if (path[0] && path[1] && path[2] && (path[3] == ':') && !path[4])
207 # else
208 if (strlen (path) == 4 && path[3] == ':')
209 # endif
210 {
211 strcpy(newpath, "U:\\DEV\\");
212 newpath[7] = path[0];
213 newpath[8] = path[1];
214 newpath[9] = path[2];
215
216 if ((path[0] == 'N' || path[0] == 'n') &&
217 (path[1] == 'U' || path[1] == 'u') &&
218 (path[2] == 'L' || path[2] == 'l'))
219 {
220 /* the device file is u:\dev\null */
221 newpath[10] = 'l';
222 newpath[11] = '\0';
223 } else
224 if ((path[0] == 'M' || path[0] == 'm') &&
225 (path[1] == 'I' || path[1] == 'i') &&
226 (path[2] == 'D' || path[2] == 'd'))
227 {
228 /* the device file is u:\dev\midi */
229 newpath[10] = 'i';
230 newpath[11] = '\0';
231 } else {
232 /* add the NULL terminator */
233 newpath[10] = '\0';
234 }
235
236 path = newpath;
237 }
238
239 /* first, check for a drive letter
240 *
241 * BUG: a '\' at the start of a symbolic link is relative to the
242 * current drive of the process, not the drive the link is located on
243 */
244 /* The check if the process runs chroot used to be inside the
245 * conditional (triggering ENOTDIR for drive specs. IMHO its
246 * cleaner to interpret it as a regular filename instead.
247 * Rationale: The path "c:/auto" is actually the same as
248 * "/c:/auto". If the process' root directory is not "/" but
249 * maybe "/home/ftp" then we should interpret the same filename
250 * now as "/home/ftp/c:/auto".
251 */
252 if (path[2] != ':' && path[1] == ':' && !cwd->root_dir)
253 {
254 char c = tolower ((int)path[0] & 0xff);
255
256 if (c >= 'a' && c <= 'z')
257 drv = c - 'a';
258 else if (c >= '1' && c <= '6')
259 drv = 26 + (c - '1');
260 else
261 goto nodrive;
262
263 # if 1
264 /* if root_dir is set drive references are forbidden
265 */
266 if (cwd->root_dir)
267 return ENOTDIR;
268 # endif
269
270 path += 2;
271
272 /* remember that we saw a drive letter
273 */
274 i = 1;
275 }
276 else
277 {
278 nodrive:
279 drv = cwd->curdrv;
280 }
281
282 /* see if the path is rooted from '\\'
283 */
284 if (DIRSEP (*path))
285 {
286 while (DIRSEP (*path))
287 path++;
288
289 /* if root_dir is set this is our start point
290 */
291 if (cwd->root_dir)
292 dup_cookie (&dir, &cwd->rootdir);
293 else
294 dup_cookie (&dir, &cwd->root[drv]);
295 }
296 else
297 {
298 if (i)
299 {
300 /* an explicit drive letter was given
301 */
302 dup_cookie (&dir, &cwd->curdir[drv]);
303 }
304 else
305 {
306 PATH2COOKIE_DB (("relpath2cookie: using relto (%p, %li, %i) for dir", relto->fs, relto->index, relto->dev));
307 dup_cookie (&dir, relto);
308 }
309 }
310
311 if (!dir.fs && !cwd->root_dir)
312 {
313 dup_cookie (&dir, &cwd->root[drv]);
314 }
315
316 if (!dir.fs)
317 {
318 DEBUG (("relpath2cookie: no file system: returning ENXIO"));
319 return ENXIO;
320 }
321
322 /* here's where we come when we've gone across a mount point
323 */
324 restart_mount:
325
326 if (!*path)
327 {
328 /* nothing more to do
329 */
330 PATH2COOKIE_DB (("relpath2cookie: no more path, returning 0"));
331
332 *res = dir;
333 return 0;
334 }
335
336
337 if (dir.fs->fsflags & FS_KNOPARSE)
338 {
339 if (!dolast)
340 {
341 PATH2COOKIE_DB (("fs is a KNOPARSE, nothing to do"));
342
343 strncpy (lastname, path, PATH_MAX-1);
344 lastname[PATH_MAX - 1] = 0;
345 r = 0;
346 *res = dir;
347 }
348 else
349 {
350 PATH2COOKIE_DB (("fs is a KNOPARSE, calling lookup"));
351
352 r = xfs_lookup (dir.fs, &dir, path, res);
353 if (r == EMOUNT)
354 {
355 /* hmmm... a ".." at a mount point, maybe
356 */
357 fcookie mounteddir;
358
359 r = xfs_root (dir.fs, dir.dev, &mounteddir);
360 if (r == 0 && drv == UNIDRV)
361 {
362 if (dir.fs == mounteddir.fs
363 && dir.index == mounteddir.index
364 && dir.dev == mounteddir.dev)
365 {
366 release_cookie (&dir);
367 release_cookie (&mounteddir);
368 dup_cookie (&dir, &cwd->root[UNIDRV]);
369 DEBUG(("path2cookie: restarting from mount point"));
370 goto restart_mount;
371 }
372 }
373 else
374 {
375 if (r == 0)
376 release_cookie (&mounteddir);
377
378 r = 0;
379 }
380 }
381
382 release_cookie (&dir);
383 }
384
385 PATH2COOKIE_DB (("relpath2cookie(2): returning %ld", r));
386 return r;
387 }
388
389
390 /* parse all but (possibly) the last component of the path name
391 *
392 * rules here: at the top of the loop, &dir is the cookie of
393 * the directory we're in now, xattr is its attributes, and res is
394 * unset at the end of the loop, &dir is unset, and either r is
395 * nonzero (to indicate an error) or res is set to the final result
396 */
397 r = xfs_getxattr (dir.fs, &dir, &xattr);
398 if (r)
399 {
400 DEBUG (("couldn't get directory attributes"));
401 release_cookie (&dir);
402 return EINTERNAL;
403 }
404
405 while (*path)
406 {
407 /* we must have a directory, since there are more things
408 * in the path
409 */
410 if (!S_ISDIR(xattr.mode))
411 {
412 PATH2COOKIE_DB (("relpath2cookie: not a directory, returning ENOTDIR"));
413 release_cookie (&dir);
414 r = ENOTDIR;
415 break;
416 }
417
418 #if 0
419 /* we must also have search permission for the directory
420 */
421 if (denyaccess (p->p_cred->ucr, &xattr, S_IXOTH))
422 {
423 DEBUG (("search permission in directory denied"));
424 release_cookie (&dir);
425 /* r = ENOTDIR; */
426 r = EACCES;
427 break;
428 }
429 #endif
430
431 /* skip slashes
432 */
433 while (DIRSEP (*path))
434 path++;
435
436 /* next, peel off the next name in the path
437 */
438 {
439 register int len;
440 register char c, *s;
441
442 len = 0;
443 s = lastname;
444 c = *path;
445 while (c && !DIRSEP (c))
446 {
447 if (len++ < PATH_MAX)
448 *s++ = c;
449 c = *++path;
450 }
451
452 *s = '\0';
453 }
454
455 /* if there are no more names in the path, and we don't want
456 * to actually look up the last name, then we're done
457 */
458 if (dolast == 0)
459 {
460 register const char *s = path;
461
462 while (DIRSEP (*s))
463 s++;
464
465 if (!*s) {
466 PATH2COOKIE_DB (("relpath2cookie: no more path, breaking"));
467 *res = dir;
468 PATH2COOKIE_DB (("relpath2cookie: *res = [%p, %i]", res->fs, res->dev));
469 break;
470 }
471 }
472
473 if (cwd->root_dir)
474 {
475 if (samefile (&dir, &cwd->rootdir)
476 && lastname[0] == '.'
477 && lastname[1] == '.'
478 && lastname[2] == '\0')
479 {
480 PATH2COOKIE_DB (("relpath2cookie: can't leave root [%s] -> forward to '.'", cwd->root_dir));
481
482 lastname[1] = '\0';
483 }
484 }
485
486 PATH2COOKIE_DB (("relpath2cookie: looking up [%s]", lastname));
487
488 r = xfs_lookup (dir.fs, &dir, lastname, res);
489 if (r == EMOUNT)
490 {
491 fcookie mounteddir;
492
493 r = xfs_root (dir.fs, dir.dev, &mounteddir);
494 if (r == 0 && drv == UNIDRV)
495 {
496 if (samefile (&dir, &mounteddir))
497 {
498 release_cookie (&dir);
499 release_cookie (&mounteddir);
500 dup_cookie (&dir, &cwd->root[UNIDRV]);
501 TRACE(("path2cookie: restarting from mount point"));
502 goto restart_mount;
503 }
504 else if (r == 0)
505 {
506 r = EINTERNAL;
507 release_cookie (&mounteddir);
508 release_cookie (&dir);
509 break;
510 }
511 }
512 else if (r == 0)
513 {
514 *res = mounteddir;
515 }
516 else
517 {
518 release_cookie (&dir);
519 break;
520 }
521 }
522 else if (r)
523 {
524 release_cookie (&dir);
525 break;
526 }
527
528 /* read the file attribute
529 */
530 r = xfs_getxattr (res->fs, res, &xattr);
531 if (r != 0)
532 {
533 DEBUG (("path2cookie: couldn't get file attributes"));
534 release_cookie (&dir);
535 release_cookie (res);
536 break;
537 }
538
539 /* check for a symbolic link
540 * - if the file is a link, and we're following links, follow it
541 */
542 if (S_ISLNK(xattr.mode) && (*path || dolast > 1))
543 {
544 {
545 r = xfs_readlink (res->fs, res, linkstuff, PATH_MAX);
546 release_cookie (res);
547 if (r)
548 {
549 DEBUG (("error reading symbolic link"));
550 release_cookie (&dir);
551 break;
552 }
553 r = relpath2cookie (p, &dir, linkstuff, follow_links, res, depth + 1);
554 release_cookie (&dir);
555 if (r)
556 {
557 DEBUG (("error following symbolic link"));
558 break;
559 }
560 }
561 dir = *res;
562 xfs_getxattr (res->fs, res, &xattr);
563 }
564 else
565 {
566 TRACE(("relpath2cookie: lookup ok, mode 0x%x", xattr.mode));
567
568 release_cookie (&dir);
569 dir = *res;
570 }
571 }
572
573 PATH2COOKIE_DB (("relpath2cookie(3): returning %ld", r));
574 return r;
575 }
576
577 long _cdecl
path2cookie(struct proc * p,const char * path,char * lastname,fcookie * res)578 path2cookie(struct proc *p, const char *path, char *lastname, fcookie *res)
579 {
580 struct cwd *cwd = p->p_cwd;
581
582 /* AHDI sometimes will keep insisting that a media change occured;
583 * we limit the number of retrys to avoid hanging the system
584 */
585 # define MAX_TRYS 4
586 int trycnt = MAX_TRYS - 1;
587
588 fcookie *dir = &cwd->curdir[cwd->curdrv];
589 long r;
590
591 restart:
592 r = relpath2cookie(p, dir, path, lastname, res, 0);
593 if (r == ECHMEDIA && trycnt--)
594 {
595 DEBUG(("path2cookie: restarting due to media change"));
596 goto restart;
597 }
598
599 return r;
600 }
601
602 /*
603 * release_cookie: tell the file system owner that a cookie is no
604 * longer in use by the kernel
605 *
606 * release_cookie doesn't release anymore unless there is no entry in
607 * the cookie cache. Otherwise, we just let the cookie get released
608 * through clobber_cookie when the cache fills or is killed through the
609 * routine above - EKL
610 */
611 void _cdecl
release_cookie(fcookie * fc)612 release_cookie (fcookie *fc)
613 {
614 if (fc)
615 {
616 FILESYS *fs;
617
618 fs = fc->fs;
619 if (fs && fs->release)
620 xfs_release (fs, fc);
621 }
622 }
623
624 /*
625 * Make a new cookie (newc) which is a duplicate of the old cookie
626 * (oldc). This may be something the file system is interested in,
627 * so we give it a chance to do the duplication; if it doesn't
628 * want to, we just copy.
629 */
630
631 void
dup_cookie(fcookie * newc,fcookie * oldc)632 dup_cookie (fcookie *newc, fcookie *oldc)
633 {
634 FILESYS *fs;
635
636 fs = oldc->fs;
637 if (fs && fs->dupcookie)
638 xfs_dupcookie (fs, newc, oldc);
639 else
640 *newc = *oldc;
641 }
642
643
644 /*
645 * check to see that a file is a directory, and that write permission
646 * is granted; return an error code, or 0 if everything is ok.
647 */
648 long
dir_access(struct ucred * cred,fcookie * dir,ushort perm,ushort * mode)649 dir_access(struct ucred *cred, fcookie *dir, ushort perm, ushort *mode)
650 {
651 XATTR xattr;
652 long r;
653
654 r = xfs_getxattr(dir->fs, dir, &xattr);
655 if (r)
656 {
657 DEBUG(("dir_access: file system returned %ld", r));
658 return r;
659 }
660
661 if (!S_ISDIR(xattr.mode))
662 {
663 DEBUG(("file is not a directory"));
664 return ENOTDIR;
665 }
666
667 #if 0
668 if (denyaccess(cred, &xattr, perm))
669 {
670 DEBUG(("no permission for directory"));
671 return EACCES;
672 }
673 #endif
674
675 *mode = xattr.mode;
676
677 return 0;
678 }
679
680 /*
681 * returns 1 if the given name contains a wildcard character
682 */
683 int
has_wild(const char * name)684 has_wild(const char *name)
685 {
686 char c;
687
688 while ((c = *name++) != 0)
689 if (c == '*' || c == '?')
690 return 1;
691
692 return 0;
693 }
694
695 /*
696 * void copy8_3(dest, src): convert a file name (src) into DOS 8.3 format
697 * (in dest). Note the following things:
698 * if a field has less than the required number of characters, it is
699 * padded with blanks
700 * a '*' means to pad the rest of the field with '?' characters
701 * special things to watch for:
702 * "." and ".." are more or less left alone
703 * "*.*" is recognized as a special pattern, for which dest is set
704 * to just "*"
705 * Long names are truncated. Any extensions after the first one are
706 * ignored, i.e. foo.bar.c -> foo.bar, foo.c.bar->foo.c.
707 */
708 void
copy8_3(char * dest,const char * src)709 copy8_3(char *dest, const char *src)
710 {
711 char fill = ' ', c;
712 int i;
713
714 if (src[0] == '.')
715 {
716 if (src[1] == 0)
717 {
718 strcpy(dest, ". . ");
719 return;
720 }
721
722 if (src[1] == '.' && src[2] == 0)
723 {
724 strcpy(dest, ".. . ");
725 return;
726 }
727 }
728
729 if (src[0] == '*' && src[1] == '.' && src[2] == '*' && src[3] == 0)
730 {
731 dest[0] = '*';
732 dest[1] = 0;
733 return;
734 }
735
736 for (i = 0; i < 8; i++)
737 {
738 c = *src++;
739
740 if (!c || c == '.')
741 break;
742 if (c == '*')
743 fill = c = '?';
744
745 *dest++ = toupper((int)c & 0xff);
746 }
747
748 while (i++ < 8)
749 *dest++ = fill;
750
751 *dest++ = '.';
752 i = 0;
753 fill = ' ';
754 while (c && c != '.')
755 c = *src++;
756
757 if (c)
758 {
759 for( ;i < 3; i++)
760 {
761 c = *src++;
762
763 if (!c || c == '.')
764 break;
765
766 if (c == '*')
767 c = fill = '?';
768
769 *dest++ = toupper((int)c & 0xff);
770 }
771 }
772
773 while (i++ < 3)
774 *dest++ = fill;
775
776 *dest = 0;
777 }
778
779 /*
780 * int pat_match(name, patrn): returns 1 if "name" matches the template in
781 * "patrn", 0 if not. "patrn" is assumed to have been expanded in 8.3
782 * format by copy8_3; "name" need not be. Any '?' characters in patrn
783 * will match any character in name. Note that if "patrn" has a '*' as
784 * the first character, it will always match; this will happen only if
785 * the original pattern (before copy8_3 was applied) was "*.*".
786 *
787 * BUGS: acts a lot like the silly TOS pattern matcher.
788 */
789 int
pat_match(const char * name,const char * template)790 pat_match(const char *name, const char *template)
791 {
792 char expname[TOS_NAMELEN+1];
793 register char *s;
794 register char c;
795
796 if (*template == '*')
797 return 1;
798
799 copy8_3(expname, name);
800
801 s = expname;
802 while ((c = *template++) != 0)
803 {
804 if (c != *s && c != '?')
805 return 0;
806 s++;
807 }
808
809 return 1;
810 }
811
812 /*
813 * int samefile(fcookie *a, fcookie *b): returns 1 if the two cookies
814 * refer to the same file or directory, 0 otherwise
815 */
816 int
samefile(fcookie * a,fcookie * b)817 samefile(fcookie *a, fcookie *b)
818 {
819 if (a->fs == b->fs && a->dev == b->dev && a->index == b->index)
820 return 1;
821
822 return 0;
823 }
824