2 * Copyright (c) 1992, 1993, 1996
3 * Berkeley Software Design, Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by Berkeley Software
18 * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * BSDI intff.c,v 2.2 1996/04/08 19:32:56 bostic Exp
32 * $FreeBSD: src/usr.bin/doscmd/intff.c,v 1.7.2.2 2002/04/25 11:04:51 tg Exp $
33 * $DragonFly: src/usr.bin/doscmd/intff.c,v 1.2 2003/06/17 04:29:26 dillon Exp $
36 #include <sys/param.h>
44 static LOL
*lol
= 0; /* DOS list-of-lists */
45 static SDA
*sda
= 0; /* DOS swappable data area */
47 /******************************************************************************
48 ** redirector functions
51 ** These are set up on entry to the redirector each time, and are referenced
52 ** by the functions here.
54 static int r_drive
,n_drive
= 0;
65 int2f11_00(regcontext_t
*REGS
)
68 R_AH
= 'U'; /* and why not? 8) */
73 ** 2f:11:1 2f:11:2 2f:11:3 2f:11:4 2f:11:5 2f:11:11 2f:11:13
75 ** Directory functions
78 int2f11_dirfn(regcontext_t
*REGS
)
80 char fname
[PATH_MAX
], tname
[PATH_MAX
];
83 error
= translate_filename(sda
->filename1
, fname
, &r_drive
);
87 if (dos_readonly(r_drive
) && (R_AL
!= 0x05))
88 return(WRITE_PROT_DISK
);
91 case 0x01: /* rmdir */
93 debug(D_REDIR
,"rmdir(%s)\n",fname
);
96 case 0x03: /* mkdir */
98 debug(D_REDIR
,"mkdir(%s)\n",fname
);
99 error
= mkdir(fname
,0777);
101 case 0x05: /* chdir */
102 debug(D_REDIR
,"chdir(%s)\n",fname
);
103 /* Note returns DOS error directly */
104 return(dos_setcwd(sda
->filename1
));
106 case 0x11: /* rename */
107 error
= translate_filename(sda
->filename2
, tname
, &r_drive
);
109 debug(D_REDIR
,"rename(%s,%s)\n",fname
,tname
);
110 error
= rename(fname
, tname
);
114 case 0x13: /* unlink */
115 debug(D_REDIR
,"unlink(%s)\n",fname
);
116 error
= unlink(fname
);
120 fatal("called int2f11_dirfn on unknown function %x\n",R_AL
);
127 return(PATH_NOT_FOUND
);
129 return(NOT_SAME_DEV
);
131 return(ACCESS_DENIED
);
143 int2f11_close(regcontext_t
*REGS __unused
)
148 debug(D_REDIR
, "close(%d)\n", fd
);
152 debug(D_REDIR
, "not last close\n");
156 return(HANDLE_INVALID
);
166 int2f11_rdwr(regcontext_t
*REGS __unused
)
174 if (lseek(fd
, r_sft
->offset
, SEEK_SET
) < 0)
177 addr
= (char *)MAKEPTR(sda
->dta_seg
, sda
->dta_off
);
181 case 0x08: /* read */
182 debug(D_REDIR
, "read(%d, %d)\n", fd
, nbytes
);
183 n
= read(fd
, addr
, nbytes
);
188 debug(D_REDIR
, "write(%d, %d)\n", fd
, nbytes
);
189 n
= write(fd
, addr
, nbytes
);
194 fatal("called int2f11_rdwr on unknown function %x\n",R_AL
);
197 R_CX
= n
; /* report count */
199 if (r_sft
->offset
> r_sft
->size
)
200 r_sft
->size
= r_sft
->offset
;
201 debug(D_REDIR
, "offset now %ld\n", r_sft
->offset
);
208 ** Get free space (like 21:36)
211 int2f11_free(regcontext_t
*REGS __unused
)
216 error
= get_space(r_drive
, &fs
);
219 R_AX
= fs
.sectors_cluster
;
220 R_BX
= fs
.total_clusters
;
221 R_CX
= fs
.bytes_sector
;
222 R_DX
= fs
.avail_clusters
;
232 int2f11_stat(regcontext_t
*REGS __unused
)
234 char fname
[PATH_MAX
];
238 error
= translate_filename(sda
->filename1
, fname
, &r_drive
);
242 if (stat(fname
, &sb
) < 0)
243 return(FILE_NOT_FOUND
);
245 R_AX
= to_dos_attr(sb
.st_mode
);
246 R_BX
= sb
.st_size
>> 16;
247 R_DI
= sb
.st_size
& 0xffff;
252 ** 2f:11:16 2f:11:17 2f:11:18 2f:11:2e
254 ** Open/create a file, closely resembles int21_open.
257 int2f11_open(regcontext_t
*REGS
)
259 char fname
[PATH_MAX
];
262 int mode
= 0; /* open mode */
263 int attr
; /* attributes of created file */
264 int action
= 0; /* what to do about file */
267 int omode
= 0; /* mode to say we opened in */
271 error
= translate_filename(sda
->filename1
, fname
, &r_drive
);
276 ** get attributes/access mode off stack : low byte is attribute, high
277 ** byte is (sometimes) used in conjunction with 'action'
279 attr
= *(u_short
*)MAKEPTR(R_SS
, R_SP
) & 0xff;
283 case 0x16: /* open */
284 action
= 0x01; /* fail if does not exist */
285 switch (sda
->open_mode
& 3) {
296 return (FUNC_NUM_IVALID
);
298 omode
= sda
->open_mode
& 0x7f;
299 debug(D_REDIR
,"open");
302 case 0x17: /* creat/creat new */
303 case 0x18: /* creat/creat new (no CDS, but we don't care)*/
306 if (attr
& 0x100) { /* creat new */
307 action
= 0x10; /* create if not exist, fail if exists */
308 debug(D_REDIR
, "creat_new");
310 action
= 0x12; /* create and destroy */
311 debug(D_REDIR
, "creat");
315 case 0x2e: /* multipurpose */
316 attr
= sda
->ext_attr
;
317 action
= sda
->ext_action
;
318 switch (sda
->ext_mode
& 3) {
329 return (FUNC_NUM_IVALID
);
331 omode
= sda
->ext_mode
& 0x7f;
332 debug(D_REDIR
,"mopen");
336 fatal("called int2f11_open for unknown function %x\n",R_AL
);
338 if (action
& 0x02) /* replace/open mode */
340 debug(D_REDIR
, "(%s) action 0x%x mode 0x%x attr 0x%x omode 0x%x \n",
341 fname
, action
, mode
, attr
, omode
);
343 if (ustat(fname
, &sb
) < 0) { /* file does not exist */
344 if ((action
& 0x10) || (attr
& 0x100)) { /* create it */
346 mode
|= O_CREAT
; /* have to create as we go */
347 status
= 0x02; /* file created */
349 return(FILE_NOT_FOUND
); /* fail */
352 if (S_ISDIR(sb
.st_mode
))
353 return(ACCESS_DENIED
);
354 if ((action
& 0x03) && !(attr
& 0x100)) { /* exists, work with it */
356 if (!S_ISREG(sb
.st_mode
)) { /* only allowed for files */
357 debug(D_FILE_OPS
,"attempt to truncate non-regular file\n");
358 return(ACCESS_DENIED
);
360 status
= 0x03; /* we're going to truncate it */
362 status
= 0x01; /* just open it */
365 return(FILE_ALREADY_EXISTS
); /* exists, fail */
369 if ((fd
= open(fname
, mode
, from_dos_attr(attr
))) < 0) {
370 debug(D_FILE_OPS
,"failed to open %s : %s\n",fname
,strerror(errno
));
371 return (ACCESS_DENIED
);
374 if (R_AL
== 0x2e) /* extended wants status returned */
377 /* black magic to populate the SFT */
379 e
= p
= sda
->filename1
+ 2; /* parse name */
381 if (*p
++ == '\\') /* look for beginning of filename */
385 for (i
= 0; i
< 8; ++i
) { /* copy name and pad with spaces */
387 r_sft
->name
[i
] = *e
++;
389 r_sft
->name
[i
] = ' ';
392 if (*e
== '.') /* skip period on short names */
395 for (i
= 0; i
< 3; ++i
) { /* copy extension and pad with spaces */
397 r_sft
->ext
[i
] = *e
++;
402 if (ustat(fname
, &sb
) < 0) /* re-stat to be accurate */
403 return(WRITE_FAULT
); /* any better ideas?! */
405 r_sft
->open_mode
= omode
; /* file open mode */
406 *(u_long
*)r_sft
->ddr_dpb
= 0; /* no parameter block */
407 r_sft
->size
= sb
.st_size
; /* current size */
408 r_sft
->fd
= fd
; /* our fd for it (hidden in starting cluster number) */
409 r_sft
->offset
= 0; /* current offset is 0 */
410 *(u_short
*)r_sft
->dir_sector
= 0; /* not local file, ignored */
411 r_sft
->dir_entry
= 0; /* not local file, ignored */
412 r_sft
->attribute
= attr
& 0xff; /* file attributes as requested */
413 r_sft
->info
= r_drive
+ 0x8040; /* hide drive number here for later reference */
414 encode_dos_file_time(sb
.st_mtime
, &r_sft
->date
, &r_sft
->time
);
415 debug(D_REDIR
,"success, fd %d status %x\n", fd
, status
);
425 int2f11_findfirst(regcontext_t
*REGS __unused
)
427 return(find_first(sda
->filename1
,sda
->attrmask
,
428 (dosdir_t
*)sda
->foundentry
,
429 (find_block_t
*)sda
->findfirst
));
438 int2f11_findnext(regcontext_t
*REGS __unused
)
440 return(find_next((dosdir_t
*)sda
->foundentry
,
441 (find_block_t
*)sda
->findfirst
));
450 int2f11_lseek(regcontext_t
*REGS
)
456 offset
= (off_t
) ((int) ((R_CX
<< 16) + R_DX
));
458 debug(D_REDIR
,"lseek(%d, 0x%qx, SEEK_END)\n", fd
, offset
);
460 if ((offset
= lseek(fd
, offset
, SEEK_END
)) < 0) {
462 return(HANDLE_INVALID
);
466 r_sft
->offset
= offset
; /* update offset in SFT */
478 int2f11_fnqual(regcontext_t
*REGS
)
482 static char errmsg
[] = "(failed)";
486 return(PATH_NOT_FOUND
);
488 savedrive
= diskdrive
; /* to get CWD for network drive */
490 fname
= (char *)MAKEPTR(R_DS
, R_SI
); /* path pointers */
491 tname
= (char *)MAKEPTR(R_ES
, R_DI
);
493 error
= dos_makepath(fname
, tname
);
497 diskdrive
= savedrive
; /* restore correct drive */
499 debug(D_REDIR
, "qualify '%s' -> '%s'\n", fname
, tname
);
506 ** Null function - we know about it but do nothing
509 int2f11_NULLFUNC(regcontext_t
*REGS __unused
)
517 ** no function - not handled here (error)
520 int2f11_NOFUNC(regcontext_t
*REGS __unused
)
525 struct intfunc_table int2f11_table
[] = {
526 { 0x00, IFT_NOSUBFUNC
, int2f11_00
, "installation check"},
527 { 0x01, IFT_NOSUBFUNC
, int2f11_dirfn
, "rmdir"},
528 { 0x02, IFT_NOSUBFUNC
, int2f11_dirfn
, "rmdir"},
529 { 0x03, IFT_NOSUBFUNC
, int2f11_dirfn
, "mkdir"},
530 { 0x04, IFT_NOSUBFUNC
, int2f11_dirfn
, "mkdir"},
531 { 0x05, IFT_NOSUBFUNC
, int2f11_dirfn
, "chdir"},
532 { 0x06, IFT_NOSUBFUNC
, int2f11_close
, "close"},
533 { 0x07, IFT_NOSUBFUNC
, int2f11_NULLFUNC
, "commit file"},
534 { 0x08, IFT_NOSUBFUNC
, int2f11_rdwr
, "read"},
535 { 0x09, IFT_NOSUBFUNC
, int2f11_rdwr
, "write"},
536 { 0x0a, IFT_NOSUBFUNC
, int2f11_NULLFUNC
, "lock region"},
537 { 0x0b, IFT_NOSUBFUNC
, int2f11_NULLFUNC
, "unlock region"},
538 { 0x0c, IFT_NOSUBFUNC
, int2f11_free
, "free space"},
539 { 0x0e, IFT_NOSUBFUNC
, int2f11_NULLFUNC
, "chmod"},
540 { 0x0f, IFT_NOSUBFUNC
, int2f11_stat
, "stat"},
541 { 0x11, IFT_NOSUBFUNC
, int2f11_dirfn
, "rename"},
542 { 0x13, IFT_NOSUBFUNC
, int2f11_dirfn
, "unlink"},
543 { 0x16, IFT_NOSUBFUNC
, int2f11_open
, "open"},
544 { 0x17, IFT_NOSUBFUNC
, int2f11_open
, "creat"},
545 { 0x18, IFT_NOSUBFUNC
, int2f11_open
, "creat"},
546 { 0x19, IFT_NOSUBFUNC
, int2f11_findfirst
, "find first"},
547 { 0x1b, IFT_NOSUBFUNC
, int2f11_findfirst
, "find first"},
548 { 0x1c, IFT_NOSUBFUNC
, int2f11_findnext
, "find next"},
549 { 0x1d, IFT_NOSUBFUNC
, int2f11_NULLFUNC
, "close all (abort)"},
550 { 0x1e, IFT_NOSUBFUNC
, int2f11_NULLFUNC
, "do redirection"},
551 { 0x1f, IFT_NOSUBFUNC
, int2f11_NULLFUNC
, "printer setup"},
552 { 0x20, IFT_NOSUBFUNC
, int2f11_NULLFUNC
, "flush all buffers"},
553 { 0x21, IFT_NOSUBFUNC
, int2f11_lseek
, "lseek"},
554 { 0x22, IFT_NOSUBFUNC
, int2f11_NULLFUNC
, "process terminated"},
555 { 0x23, IFT_NOSUBFUNC
, int2f11_fnqual
, "qualify filename"},
556 { 0x24, IFT_NOSUBFUNC
, int2f11_NOFUNC
, "turn off printer"},
557 { 0x25, IFT_NOSUBFUNC
, int2f11_NOFUNC
, "printer mode"},
558 { 0x2d, IFT_NOSUBFUNC
, int2f11_NOFUNC
, "extended attributes"},
559 { 0x2e, IFT_NOSUBFUNC
, int2f11_open
, "extended open/create"},
563 static int int2f11_fastlookup
[256];
565 /******************************************************************************
568 ** The DOS redirector interface.
572 ** Verify that the drive being referenced is one we are handling, and
573 ** establish some state for upcoming functions.
575 ** Returns 1 if we should handle this request.
577 ** XXX this is rather inefficient, but much easier to read than the previous
581 int2f11_validate(regcontext_t
*REGS
)
584 const char *path
= NULL
;
587 /* defaults may help trap problems */
592 /* some functions we accept regardless */
594 case 0x00: /* installation check */
595 case 0x23: /* qualify path */
596 case 0x1c: /* XXX really only valid if a search already started... */
600 /* Where's the CDS? */
602 case 0x01: /* referenced by the SDA */
613 r_cds
= (CDS
*)MAKEPTR(sda
->cds_seg
, sda
->cds_off
);
616 case 0x0c: /* in es:di */
618 r_cds
= (CDS
*)MAKEPTR(R_ES
, R_DI
);
622 /* Where's the SFT? */
624 case 0x06: /* in es:di */
636 r_sft
= (SFT
*)MAKEPTR(R_ES
, R_DI
);
642 case 0x01: /* get drive from fully-qualified path in SDA */
658 path
= sda
->filename1
;
661 case 0x06: /* get drive from SFT (we put it here when file was opened) */
669 r_drive
= r_sft
->info
& 0x1f;
673 if (path
) { /* we have a path and need to determine the drive it refers to */
675 if (path
[1] != ':') { /* must be fully qualified; we cannot handle this */
676 debug(D_REDIR
,"attempt to work non-absolute path %s\n",path
);
680 /* translate letter to drive number */
681 r_drive
= drlton(path
[0]);
686 /* do we handle this drive? */
687 if (dos_getcwd(r_drive
)) {
688 n_drive
= r_drive
; /* XXX GROSTIC HACK ALERT */
692 debug(D_REDIR
,"%s -> drive %c func %x (%sus)\n",
693 path
, drntol(r_drive
), func
, doit
?"":"not ");
695 /* so do we deal with this one? */
701 int2f_11(regcontext_t
*REGS
)
706 if (!sda
) { /* not initialised yet */
707 error
= FUNC_NUM_IVALID
;
710 idx
= intfunc_find(int2f11_table
, int2f11_fastlookup
, R_AL
, 0);
712 debug(D_ALWAYS
,"no handler for int2f:11:%x\n", R_AL
);
718 if (!int2f11_validate(REGS
)) { /* determine whether we handle this request */
719 error
= -1; /* not handled by us */
721 debug(D_REDIR
, "REDIR: %02x (%s)\n",
722 int2f11_table
[idx
].func
, int2f11_table
[idx
].desc
);
723 /* call the handler */
724 error
= int2f11_table
[idx
].handler(REGS
);
726 debug(D_REDIR
, "REDIR: returns %d (%s)\n",
727 error
, ((error
>= 0) && (error
<= dos_ret_size
)) ? dos_return
[error
] : "unknown");
741 /******************************************************************************
744 ** intff is the (secret, proprietary, internal, evil) call to
745 ** initialise the redirector.
748 install_drive(int drive
, u_char
*path
)
752 /* check that DOS considers this a valid drive */
753 if (drive
< 0 || drive
>= lol
->lastdrive
) {
754 debug(D_REDIR
, "Drive %c beyond limit of %c\n",
755 drntol(drive
), drntol(lol
->lastdrive
- 1));
759 /* get the CDS for this drive */
760 cds
= (CDS
*)MAKEPTR(lol
->cds_seg
, lol
->cds_offset
);
763 #if 0 /* XXX looks OK to me - mjs */
764 if (cds
->flag
& (CDS_remote
| CDS_ready
)) {
765 debug(D_REDIR
, "Drive %c already installed\n", drntol(drive
));
770 debug(D_REDIR
, "Installing %c: as %s\n", drntol(drive
), path
);
772 cds
->flag
|= CDS_remote
| CDS_ready
| CDS_notnet
;
773 cds
->path
[0] = drntol(drive
);
777 cds
->offset
= 2; /* offset of \ in current path field */
786 /* for all possible drives */
787 for (drive
= 0; drive
< 26; ++drive
) {
788 if ((path
= dos_getpath(drive
)) != 0) /* assigned to a path? */
789 install_drive(drive
, path
); /* make it visible to DOS */
795 intff(regcontext_t
*REGS
)
798 if (lol
&& sda
) { /* already been called? */
799 debug(D_REDIR
, "redirector duplicate install ignored");
802 lol
= (LOL
*)MAKEPTR(R_BX
, R_DX
); /* where DOS keeps its goodies */
803 sda
= (SDA
*)MAKEPTR(R_DI
, R_SI
);
806 /* initialise dispatcher */
807 intfunc_init(int2f11_table
, int2f11_fastlookup
);