diff -u --recursive --new-file v1.1.34/linux/CREDITS linux/CREDITS --- v1.1.34/linux/CREDITS Mon May 23 12:14:21 1994 +++ linux/CREDITS Sat Jul 23 12:58:36 1994 @@ -53,7 +53,6 @@ N: Hennus Bergman E: hennus@sky.nl.mugnet.org [My uucp-fed Linux box at home] -E: csg279@wing.rug.nl [Alternate address] D: Author and maintainer of the QIC-02 tape driver S: The Netherlands diff -u --recursive --new-file v1.1.34/linux/Makefile linux/Makefile --- v1.1.34/linux/Makefile Sat Jul 23 17:38:36 1994 +++ linux/Makefile Sat Jul 23 13:03:17 1994 @@ -1,6 +1,6 @@ VERSION = 1 PATCHLEVEL = 1 -SUBLEVEL = 34 +SUBLEVEL = 35 all: Version zImage diff -u --recursive --new-file v1.1.34/linux/config.in linux/config.in --- v1.1.34/linux/config.in Thu Jul 21 08:18:00 1994 +++ linux/config.in Sun Jul 24 17:53:38 1994 @@ -150,6 +150,7 @@ bool 'Second extended fs support' CONFIG_EXT2_FS y bool 'xiafs filesystem support' CONFIG_XIA_FS n bool 'msdos fs support' CONFIG_MSDOS_FS y +bool 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS n bool '/proc filesystem support' CONFIG_PROC_FS y if [ "$CONFIG_INET" = "y" ]; then bool 'NFS filesystem support' CONFIG_NFS_FS y @@ -169,7 +170,22 @@ bool 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE n bool 'ATIXL busmouse support' CONFIG_ATIXL_BUSMOUSE n bool 'Selection (cut and paste for virtual consoles)' CONFIG_SELECTION n -bool 'QIC-02 tape support' CONFIG_TAPE_QIC02 n + +bool 'QIC-02 tape support' CONFIG_QIC02_TAPE n +if [ "$CONFIG_QIC02_TAPE" = "y" ]; then +bool 'Do you want runtime configuration for QIC-02' CONFIG_QIC02_DYNCONF y +if [ "$CONFIG_QIC02_DYNCONF" != "y" ]; then + +comment '>>> Edit configuration parameters in ./include/linux/tpqic02.h!' + +else + +comment '>>> Setting runtime QIC-02 configuration is done with qic02conf' +comment '>>> Which is available from ftp://ftp.funet.fi/pub/OS/Linux/BETA/QIC-02/' + +fi +fi + bool 'QIC-117 tape support' CONFIG_FTAPE n if [ "$CONFIG_FTAPE" = "y" ]; then int ' number of ftape buffers' NR_FTAPE_BUFFERS 3 diff -u --recursive --new-file v1.1.34/linux/drivers/char/Makefile linux/drivers/char/Makefile --- v1.1.34/linux/drivers/char/Makefile Tue May 24 00:34:46 1994 +++ linux/drivers/char/Makefile Sat Jul 23 12:58:36 1994 @@ -58,7 +58,7 @@ SRCS := $(SRCS) psaux.c endif -ifdef CONFIG_TAPE_QIC02 +ifdef CONFIG_QIC02_TAPE OBJS := $(OBJS) tpqic02.o SRCS := $(SRCS) tpqic02.c endif diff -u --recursive --new-file v1.1.34/linux/drivers/char/console.c linux/drivers/char/console.c --- v1.1.34/linux/drivers/char/console.c Fri Jul 22 16:41:01 1994 +++ linux/drivers/char/console.c Sun Jul 24 17:39:43 1994 @@ -156,7 +156,7 @@ /* misc */ unsigned long vc_ques : 1; unsigned long vc_need_wrap : 1; - unsigned long vc_report_mouse : 1; + unsigned long vc_report_mouse : 2; unsigned long vc_tab_stop[5]; /* Tab stops. 160 columns. */ unsigned char * vc_translate; unsigned char * vc_G0_charset; @@ -848,12 +848,15 @@ clr_kbd(decarm); break; case 9: - report_mouse = on_off; + report_mouse = on_off ? 1 : 0; break; case 25: /* Cursor on/off */ deccm = on_off; set_cursor(currcons); break; + case 1000: + report_mouse = on_off ? 2 : 0; + break; } else switch(par[i]) { /* ANSI modes set/reset */ case 4: /* Insert Mode on/off */ decim = on_off; @@ -1858,11 +1861,11 @@ } /* invoked via ioctl(TIOCLINUX) */ -int mouse_reporting_p(void) +int mouse_reporting(void) { int currcons = fg_console; - return ((report_mouse) ? 0 : -EINVAL); + return report_mouse; } /* set the current selection. Invoked by ioctl(). */ diff -u --recursive --new-file v1.1.34/linux/drivers/char/mem.c linux/drivers/char/mem.c --- v1.1.34/linux/drivers/char/mem.c Thu Jul 21 08:18:05 1994 +++ linux/drivers/char/mem.c Sat Jul 23 12:58:36 1994 @@ -411,8 +411,8 @@ #ifdef CONFIG_SOUND mem_start = soundcard_init(mem_start); #endif -#if CONFIG_TAPE_QIC02 - mem_start = tape_qic02_init(mem_start); +#if CONFIG_QIC02_TAPE + mem_start = qic02_tape_init(mem_start); #endif /* * Rude way to allocate kernel memory buffer for tape device diff -u --recursive --new-file v1.1.34/linux/drivers/char/tpqic02.c linux/drivers/char/tpqic02.c --- v1.1.34/linux/drivers/char/tpqic02.c Tue May 31 12:48:16 1994 +++ linux/drivers/char/tpqic02.c Sat Jul 23 12:58:37 1994 @@ -1,9 +1,10 @@ -/* $Id: tpqic02.c,v 0.2.1.21 1993/06/18 19:04:33 root Exp root $ +/* $Id: tpqic02.c,v 0.4.1.4 1994/07/21 02:15:45 root Exp root $ * - * Driver for tape drive support for Linux-i386 0.99.12. + * Driver for tape drive support for Linux-i386 1.1.30 * - * Copyright (c) 1993 by H. H. Bergman. All rights reserved. - * Current e-mail address: csg279@wing.rug.nl + * Copyright (c) 1992, 1993, 1994 by H. H. Bergman. All rights reserved. + * Current e-mail address: hennus@sky.nl.mugnet.org [This is a UUCP link.] + * Secondary e-mail address: csg279@wing.rug.nl [IP connected, but flaky] * [If you are unable to reach me directly, try the TAPE mailing list * channel on linux-activists@niksula.hut.fi using "X-Mn-Key: TAPE" as * the first line in your message.] @@ -18,6 +19,13 @@ * Use this code at your own risk. Don't blame me if it destroys your data! * Make sure you have a backup before you try this code. * + * If you make changes to my code and redistribute it in source or binary + * form you must make it clear to even casual users of your code that you + * have modified my code, clearly point out what the changes exactly are + * (preferably in the form of a context diff file), how to undo your changes, + * where the original can be obtained, and that complaints/requests about the + * modified code should be directed to you instead of me. + * * This driver was partially inspired by the 'wt' driver in the 386BSD * source distribution, which carries the following copyright notice: * @@ -27,6 +35,46 @@ * You are not allowed to change this line nor the text above. * * $Log: tpqic02.c,v $ + * Revision 0.4.1.4 1994/07/21 02:15:45 root + * ifdef'd DDI. Exception masks. + * + * Revision 0.4.1.3 1994/05/03 01:49:09 root + * Initial attempt at Mountain support for the Mountain 7150. + * Based on patches provided by Erik Jacobson. + * + * Revision 0.4.1.2 1994/03/18 21:16:50 root + * Many driver messages can now be turned off (runtime selectable). + * + * Revision 0.4.1.1 1994/02/16 19:47:22 root + * First stab at runtime debug-variable. + * + * Revision 0.4 1994/02/15 01:53:16 root + * DYNCONF mark II. + * Minor cleanups. + * + * Revision 0.3 1994/02/07 01:23:16 root + * More improved DYNCONF. + * Archive changes & some cleanups by Eddy Olk. + * Removed status_open, more cleanups, misc other. + * + * Revision 0.2.1.25 1994/01/24 02:01:33 root + * Changed tape_qic02 to QIC02_TAPE. + * Changes to prepare for DYNCONF. + * + * Revision 0.2.1.24 1994/01/23 07:27:18 root + * Attempt to remove compilation warnings, G++ bug, + * Linus changed TAPE_QIC02 to QIC02_TAPE. + * + * Revision 0.2.1.23 1994/01/20 23:49:28 root + * Changed some exception decoding stuff. + * TP_HAVE_SEEK, TP_HAVE_DENS. byte_swap_w() on arg, not global. + * Attempt to fix cartridge-changed-problem for 2150L. + * Release irq and dma reservations if initial reset fails. + * + * Revision 0.2.1.22 1994/01/19 20:56:55 root + * Speed measuring stuff moved from aperf.h to delay.h. + * BogoMips (tm) introduced by Linus. + * * Revision 0.2.1.21 1993/06/18 19:04:33 root * minor fixes for 0.99.10. * @@ -146,7 +194,7 @@ #include /* skip this driver if not required for this configuration */ -#if CONFIG_TAPE_QIC02 +#if CONFIG_QIC02_TAPE /* #define TDEBUG @@ -171,29 +219,45 @@ #include /* check existence of required configuration parameters */ -#if !defined(TAPE_QIC02_PORT) || \ - !defined(TAPE_QIC02_IRQ) || \ - !defined(TAPE_QIC02_DMA) -#error tape_qic02 configuration error +#if !defined(QIC02_CMD_PORT) || \ + !defined(QIC02_TAPE_IRQ) || \ + !defined(QIC02_TAPE_DMA) +#error qic02_tape configuration error #endif -#define TPQIC_NAME "tpqic02" +#define TPQIC02_NAME "tpqic02" /* Linux outb() commands have (value,port) as parameters. * One might expect (port,value) instead, so beware! */ +#ifdef CONFIG_QIC02_DYNCONF +/* This may hold the dynamic configuration info for the interface + * card+drive info in future versions. + */ +struct mtconfiginfo qic02_tape_dynconf = { 0, }; /* user settable */ +struct qic02_ccb qic02_tape_ccb = { 0, }; /* private stuff */ + +#else + +unsigned long qic02_tape_debug; + +# if ((QIC02_TAPE_IFC!=WANGTEK) && (QIC02_TAPE_IFC!=ARCHIVE) && (QIC02_TAPE_IFC!=MOUNTAIN)) +# error No valid interface card specified +# endif +#endif + static volatile int ctlbits = 0; /* control reg bits for tape interface */ -static struct wait_queue *tape_qic02_transfer = NULL; /* sync rw with interrupts */ +static struct wait_queue *qic02_tape_transfer = NULL; /* sync rw with interrupts */ static volatile struct mtget ioctl_status; /* current generic status */ static volatile struct tpstatus tperror; /* last drive status */ -static char rcs_revision[] = "$Revision: 0.2.1.21 $"; -static char rcs_date[] = "$Date: 1993/06/18 19:04:33 $"; +static char rcs_revision[] = "$Revision: 0.4.1.4 $"; +static char rcs_date[] = "$Date: 1994/07/21 02:15:45 $"; /* Flag bits for status and outstanding requests. * (Could all be put in one bit-field-struct.) @@ -201,7 +265,7 @@ * by an interrupt. */ static volatile flag status_dead = YES; /* device is legally dead until proven alive */ -static flag status_open = NO; /* in use or not */ +static flag status_zombie = YES; /* it's `zombie' until irq/dma allocated */ static volatile flag status_bytes_wr = NO; /* write FM at close or not */ static volatile flag status_bytes_rd = NO; /* (rd|wr) used for rewinding */ @@ -243,10 +307,8 @@ static flag reported_write_eof = NO; -#ifdef TP_HAVE_SEEK /* This is for doing `mt seek ' */ -static char seek_addr_buf[SEEK_BUF_SIZE]; -#endif +static char seek_addr_buf[AR_SEEK_BUF_SIZE]; /* In write mode, we have to write a File Mark after the last block written, @@ -259,8 +321,8 @@ /* This is the actual kernel buffer where the interrupt routines read * from/write to. It is needed because the DMA channels 1 and 3 cannot - * access the user buffers. [The kernel buffer must reside in the lower - * 1MBytes of system memory because of the DMA controller.] + * always access the user buffers. [The kernel buffer must reside in the + * lower 16MBytes of system memory because of the DMA controller.] * The user must ensure that a large enough buffer is passed to the * kernel, in order to reduce tape repositioning. * @@ -268,7 +330,7 @@ * at 512 bytes, to prevent problems with 64k boundaries. */ -static volatile char tape_qic02_buf[TPQBUF_SIZE+TAPE_BLKSIZE]; +static volatile char qic02_tape_buf[TPQBUF_SIZE+TAPE_BLKSIZE]; /* A really good compiler would be able to align this at 512 bytes... :-( */ static unsigned long buffaddr; /* aligned physical address of buffer */ @@ -295,29 +357,35 @@ * "Exception Status Summary" in QIC-02 rev F, but some changes * were required to make it work with real-world drives. * - * Exception 1 (CNI) is changed to also cover status 0x00e0 (mask USL), + * Exception 2 (CNI) is changed to also cover status 0x00e0 (mask USL), * Exception 4 (EOM) is changed to also cover status 0x8288 (mask EOR), * Exception 11 (FIL) is changed to also cover status 0x0089 (mask EOM). * Exception 15 (EOR) is added for seek-to-end-of-data (catch EOR), * Exception 16 (BOM) is added for beginning-of-media (catch BOM). + * + * Had to swap EXC_NDRV and EXC_NCART to ensure that extended EXC_NCART + * (because of the incorrect Wangtek status code) doesn't catch the + * EXC_NDRV first. */ static struct exception_list_type { unsigned short mask, code; char *msg; + /* EXC_nr attribute should match with tpqic02.h */ } exception_list[] = { {0, 0, "Unknown exception status code", /* extra: 0 */}, + {~(0), TP_ST0|TP_CNI|TP_USL|TP_WRP, + "Drive not online" /* 1 */}, + /* Drive presence goes before cartridge presence. */ {~(TP_WRP|TP_USL), TP_ST0|TP_CNI, /* My Wangtek 5150EQ sometimes reports a status code * of 0x00e0, which is not a valid exception code, but * I think it should be recognized as "NO CARTRIDGE". */ - "Cartridge not in place" /* 1 */}, - {-1, TP_ST0|TP_CNI|TP_USL|TP_WRP, - "Drive not online" /* 2 */}, - {~(TP_ST1|TP_BOM), TP_ST0|TP_WRP, + "Cartridge not in place" /* 2 */}, + {(unsigned short) ~(TP_ST1|TP_BOM), (TP_ST0|TP_WRP), "Write protected cartridge" /* 3 */}, - {~(TP_ST1|TP_EOR), TP_ST0|TP_EOM, + {(unsigned short) ~(TP_ST1|TP_EOR), (TP_ST0|TP_EOM), "End of media" /* 4 */}, {~TP_WRP, TP_ST0|TP_UDA| TP_ST1|TP_BOM, "Read or Write abort. Rewind tape." /* 5 */}, @@ -349,26 +417,20 @@ /* 15 is returned when SEEKEOD completes successfully */ {~(TP_WRP|TP_ST0), TP_ST1|TP_BOM, "Beginning of media" /* extra: 16 */} -#ifdef CYPHER_BUG - /* Perhaps the Cypher driver clears the TP_BOM bit after the - * status has been read? The QIC-02 specs explicitly state that - * the BOM bit should remain set as long as the tape is logically - * at the beginning of the tape. - */ - ,{-1, TP_ST1, - "Hmm, this must be Cypher drive... Aaargh" /* extra */} -#endif }; #define NR_OF_EXC (sizeof(exception_list)/sizeof(struct exception_list_type)) -static void tpqputs(char *s) +static void tpqputs(unsigned long flags, char *s) { - printk(TPQIC_NAME ": %s\n", s); + if ((flags & TPQD_ALWAYS) || (flags & QIC02_TAPE_DEBUG)) + printk(TPQIC02_NAME ": %s\n", s); } /* tpqputs */ + + /* Perform byte order swapping for a 16-bit word. * * [FIXME] This should probably be in include/asm/ @@ -390,24 +452,27 @@ */ static void ifc_init(void) { -#if TAPE_QIC02_IFC == WANGTEK - ctlbits = WT_CTL_ONLINE; /* online */ - outb_p(ctlbits, QIC_CTL_PORT); - -#elif TAPE_QIC02_IFC == ARCHIVE - ctlbits = 0; /* no interrupts yet */ - outb_p(ctlbits, QIC_CTL_PORT); - outb_p(0, AR_RESET_DMA_PORT); /* dummy write to reset DMA */ -#else -# error No valid interface card specified -#endif + if (QIC02_TAPE_IFC == WANGTEK) /* || (QIC02_TAPE_IFC == EVEREX) */ { + ctlbits = WT_CTL_ONLINE; /* online */ + outb_p(ctlbits, QIC02_CTL_PORT); + + } else if (QIC02_TAPE_IFC == ARCHIVE) { + ctlbits = 0; /* no interrupts yet */ + outb_p(ctlbits, QIC02_CTL_PORT); + outb_p(0, AR_RESET_DMA_PORT); /* dummy write to reset DMA */ + + } else /* MOUNTAIN */ { + ctlbits = MTN_CTL_ONLINE; /* online, and logic enabled */ + outb_p(ctlbits, QIC02_CTL_PORT); + } } /* ifc_init */ static void report_exception(unsigned n) { - if (n >= NR_OF_EXC) { tpqputs("Oops -- report_exception"); n = 0; } - printk(TPQIC_NAME ": sense: %s\n", exception_list[n].msg); + if (n >= NR_OF_EXC) { tpqputs(TPQD_ALWAYS, "Oops -- report_exception"); n = 0; } + if (TPQDBG(SENSE_TEXT) || n==0) + printk(TPQIC02_NAME ": sense: %s\n", exception_list[n].msg); } /* report_exception */ @@ -416,7 +481,7 @@ * exception table (`exception_list[]'). * It is assumed that s!=0. */ -static int decode_exception_nr(short s) /* s must be short, because of sign-extension */ +static int decode_exception_nr(unsigned s) { int i; @@ -424,7 +489,7 @@ if ((s & exception_list[i].mask)==exception_list[i].code) return i; } - tpqputs("decode_exception_nr: exception not recognized"); + printk(TPQIC02_NAME ": decode_exception_nr: exception(%x) not recognized\n", s); return 0; } /* decode_exception_nr */ @@ -472,7 +537,7 @@ else /* 8: Read error. No data detected. CONTINUABLE */ n = 8; } else { /* 7: Read error. Cannot recover block, filler substituted. CONTINUABLE */ - tpqputs("[Bad block -- filler data transferred.]"); + tpqputs(TPQD_ALWAYS, "[Bad block -- filler data transferred.]"); n = 7; } } @@ -484,14 +549,14 @@ * This is why some people prefer not to * use compression on backups... */ - tpqputs("[CRC failed!]"); + tpqputs(TPQD_ALWAYS, "[CRC failed!]"); n = 6; } } } else if (s & TP_FIL) { if (s & TP_MBD) { /* 14: Marginal block detected. CONTINUABLE */ - tpqputs("[Marginal block]"); + tpqputs(TPQD_ALWAYS, "[Marginal block]"); n = 14; } else /* 11: File mark detected. CONTINUABLE */ n = 11; @@ -519,14 +584,14 @@ status_eom_detected = NO; } else if (exnr==EXC_XFILLER) - tpqputs("[Bad block -- filler data transferred.]"); + tpqputs(TPQD_ALWAYS, "[Bad block -- filler data transferred.]"); else if (exnr==EXC_XBAD) - tpqputs("[CRC failed!]"); + tpqputs(TPQD_ALWAYS, "[CRC failed!]"); else if (exnr==EXC_MARGINAL) { /* A marginal block behaves much like a FM. * User may continue reading, if desired. */ - tpqputs("[Marginal block]"); + tpqputs(TPQD_ALWAYS, "[Marginal block]"); doing_read = NO; } else if (exnr==EXC_FM) doing_read = NO; @@ -535,7 +600,7 @@ static inline int is_exception(void) { - return (inb(QIC_STAT_PORT) & QIC_STAT_EXCEPTION) == 0; + return (inb(QIC02_STAT_PORT) & QIC02_STAT_EXCEPTION) == 0; } /* is_exception */ @@ -547,7 +612,11 @@ { ifc_init(); /* reset interface card */ - outb_p(ctlbits | QIC_CTL_RESET, QIC_CTL_PORT); /* assert reset */ + /* assert reset */ + if (QIC02_TAPE_IFC == MOUNTAIN) + outb_p(ctlbits & ~MTN_QIC02_CTL_RESET_NOT, QIC02_CTL_PORT); + else /* WANGTEK, ARCHIVE */ + outb_p(ctlbits | QIC02_CTL_RESET, QIC02_CTL_PORT); /* Next, we need to wait >=25 usec. */ udelay(30); @@ -560,17 +629,22 @@ doing_read = doing_write = NO; ioctl_status.mt_fileno = ioctl_status.mt_blkno = 0; - outb_p(ctlbits & ~QIC_CTL_RESET, QIC_CTL_PORT); /* de-assert reset */ + /* de-assert reset */ + if (QIC02_TAPE_IFC == MOUNTAIN) + outb_p(ctlbits | MTN_QIC02_CTL_RESET_NOT, QIC02_CTL_PORT); + else + outb_p(ctlbits & ~QIC02_CTL_RESET, QIC02_CTL_PORT); + /* KLUDGE FOR G++ BUG */ - { int stat = inb_p(QIC_STAT_PORT); - status_dead = ((stat & QIC_STAT_RESETMASK) != QIC_STAT_RESETVAL); } + { int stat = inb_p(QIC02_STAT_PORT); + status_dead = ((stat & QIC02_STAT_RESETMASK) != QIC02_STAT_RESETVAL); } /* if successful, inb(STAT) returned RESETVAL */ - if (status_dead) - printk(TPQIC_NAME ": reset failed!\n"); + if (status_dead == YES) + printk(TPQIC02_NAME ": reset failed!\n"); else if (verbose) - printk(TPQIC_NAME ": reset successful\n"); + printk(TPQIC02_NAME ": reset successful\n"); - return (status_dead)? TE_DEAD : TE_OK; + return (status_dead == YES)? TE_DEAD : TE_OK; } /* tape_reset */ @@ -578,7 +652,7 @@ /* Notify tape drive of a new command. It only waits for the * command to be accepted, not for the actual command to complete. * - * Before calling this routine, QIC_CMD_PORT must have been loaded + * Before calling this routine, QIC02_CMD_PORT must have been loaded * with the command to be executed. * After this routine, the exception bit must be checked. * This routine is also used by rdstatus(), so in that case, any exception @@ -588,43 +662,43 @@ { int i; - outb_p(cmd, QIC_CMD_PORT); /* output the command */ + outb_p(cmd, QIC02_CMD_PORT); /* output the command */ /* wait 1 usec before asserting /REQUEST */ udelay(1); if ((!ignore_ex) && is_exception()) { - tpqputs("*** exception detected in notify_cmd"); + tpqputs(TPQD_ALWAYS, "*** exception detected in notify_cmd"); /** force a reset here **/ if (tape_reset(1)==TE_DEAD) return TE_DEAD; if (is_exception()) { - tpqputs("exception persists after reset."); - tpqputs(" ^ exception ignored."); + tpqputs(TPQD_ALWAYS, "exception persists after reset."); + tpqputs(TPQD_ALWAYS, " ^ exception ignored."); } } - outb_p(ctlbits | QIC_CTL_REQUEST, QIC_CTL_PORT); /* set request bit */ + outb_p(ctlbits | QIC02_CTL_REQUEST, QIC02_CTL_PORT); /* set request bit */ i = TAPE_NOTIFY_TIMEOUT; /* The specs say this takes about 500 usec, but there is no upper limit! * If the drive were busy retensioning or something like that, * it could be *much* longer! */ - while ((inb_p(QIC_STAT_PORT) & QIC_STAT_READY) && (--i>0)) + while ((inb_p(QIC02_STAT_PORT) & QIC02_STAT_READY) && (--i>0)) /*skip*/; /* wait for ready */ if (i==0) { - tpqputs("timed out waiting for ready in notify_cmd"); + tpqputs(TPQD_ALWAYS, "timed out waiting for ready in notify_cmd"); status_dead = YES; return TE_TIM; } - outb_p(ctlbits & ~QIC_CTL_REQUEST, QIC_CTL_PORT); /* reset request bit */ + outb_p(ctlbits & ~QIC02_CTL_REQUEST, QIC02_CTL_PORT); /* reset request bit */ i = TAPE_NOTIFY_TIMEOUT; /* according to the specs this one should never time-out */ - while (((inb_p(QIC_STAT_PORT) & QIC_STAT_READY) == 0) && (--i>0)) + while (((inb_p(QIC02_STAT_PORT) & QIC02_STAT_READY) == 0) && (--i>0)) /*skip*/; /* wait for not ready */ if (i==0) { - tpqputs("timed out waiting for !ready in notify_cmd"); + tpqputs(TPQD_ALWAYS, "timed out waiting for !ready in notify_cmd"); status_dead = YES; return TE_TIM; } @@ -647,9 +721,9 @@ * First, busy wait a few usec: */ spin_t = 50; - while (((stat = inb_p(QIC_STAT_PORT) & QIC_STAT_MASK) == QIC_STAT_MASK) && (--spin_t>0)) + while (((stat = inb_p(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_STAT_MASK) && (--spin_t>0)) /*SKIP*/; - if ((stat & QIC_STAT_READY) == 0) + if ((stat & QIC02_STAT_READY) == 0) return TE_OK; /* covers 99.99% of all calls */ /* Then use schedule() a few times */ @@ -659,9 +733,9 @@ timeout -= spin_t; spin_t += jiffies; - while (((stat = inb_p(QIC_STAT_PORT) & QIC_STAT_MASK) == QIC_STAT_MASK) && (jiffiestimeout = jiffies + 30; /* nap 0.30 sec between checks, */ current->state = TASK_INTERRUPTIBLE; @@ -682,13 +756,13 @@ } /* don't use jiffies for this test because it may have changed by now */ - if ((stat & QIC_STAT_MASK) == QIC_STAT_MASK) { - tpqputs("wait_for_ready() timed out"); + if ((stat & QIC02_STAT_MASK) == QIC02_STAT_MASK) { + tpqputs(TPQD_ALWAYS, "wait_for_ready() timed out"); return TE_TIM; } - if ((stat & QIC_STAT_EXCEPTION) == 0) { - tpqputs("exception detected after waiting_for_ready"); + if ((stat & QIC02_STAT_EXCEPTION) == 0) { + tpqputs(TPQD_ALWAYS, "exception detected after waiting_for_ready"); return TE_EX; } else { return TE_OK; @@ -696,6 +770,7 @@ } /* wait_for_ready */ + /* Send some data to the drive */ static int send_qic02_data(char sb[], unsigned size, int ignore_ex) { @@ -727,13 +802,13 @@ { int stat; - stat = inb_p(QIC_STAT_PORT); - if ((stat & QIC_STAT_EXCEPTION) == 0) { /* if exception */ - tpqputs("send_qic02_cmd: Exception!"); + stat = inb_p(QIC02_STAT_PORT); + if ((stat & QIC02_STAT_EXCEPTION) == 0) { /* if exception */ + tpqputs(TPQD_ALWAYS, "send_qic02_cmd: Exception!"); return TE_EX; } - if (stat & QIC_STAT_READY) { /* if not ready */ - tpqputs("send_qic02_cmd: not Ready!"); + if (stat & QIC02_STAT_READY) { /* if not ready */ + tpqputs(TPQD_ALWAYS, "send_qic02_cmd: not Ready!"); return TE_ERR; } @@ -746,12 +821,13 @@ stat = notify_cmd(cmd, ignore_ex); /* tell drive new command was loaded, */ /* inherit exception check. */ - if (cmd == QCMDV_SEEK_BLK) { + if (TP_HAVE_SEEK && (cmd == AR_QCMDV_SEEK_BLK)) { /* This one needs to send 3 more bytes, MSB first */ stat = send_qic02_data(seek_addr_buf, sizeof(seek_addr_buf), ignore_ex); } + if (stat != TE_OK) { - tpqputs("send_qic02_cmd failed"); + tpqputs(TPQD_ALWAYS, "send_qic02_cmd failed"); } return stat; } /* send_qic02_cmd */ @@ -775,17 +851,17 @@ * task switch is much longer than we usually have to wait here. */ n = 1000; /* 500 is not enough on a 486/33 */ - while ((n>0) && ((inb_p(QIC_STAT_PORT) & QIC_STAT_MASK) == QIC_STAT_MASK)) + while ((n>0) && ((inb_p(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_STAT_MASK)) n--; /* wait for ready or exception or timeout */ if (n==0) { /* n (above) should be chosen such that on your machine * you rarely ever see the message below, and it should * be small enough to give reasonable response time.] */ - tpqputs("waiting looong in rdstatus() -- drive dead?"); - while ((inb_p(QIC_STAT_PORT) & QIC_STAT_MASK) == QIC_STAT_MASK) + tpqputs(TPQD_ALWAYS, "waiting looong in rdstatus() -- drive dead?"); + while ((inb_p(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_STAT_MASK) schedule(); - tpqputs("finished waiting in rdstatus()"); + tpqputs(TPQD_ALWAYS, "finished waiting in rdstatus()"); } (void) notify_cmd(qcmd, 1); /* send read status command */ @@ -794,38 +870,38 @@ */ if (TP_DIAGS(current_tape_dev)) - printk(TPQIC_NAME ": reading status bytes: "); + printk(TPQIC02_NAME ": reading status bytes: "); for (q=stp; q20 usec */ - outb_p(ctlbits & ~QIC_CTL_REQUEST, QIC_CTL_PORT); /* un-set request */ + outb_p(ctlbits & ~QIC02_CTL_REQUEST, QIC02_CTL_PORT); /* un-set request */ } /* Specs say we should wait for READY here. * My drive doesn't seem to need it here yet, but others do? */ - while (inb_p(QIC_STAT_PORT) & QIC_STAT_READY) + while (inb_p(QIC02_STAT_PORT) & QIC02_STAT_READY) /*skip*/; /* wait for ready */ if (TP_DIAGS(current_tape_dev)) @@ -840,12 +916,12 @@ * The `.dec' and `.urc' fields are in MSB-first byte-order, * so they have to be swapped first. */ -static int get_status(char *stp) +static int get_status(volatile struct tpstatus *stp) { - int stat = rdstatus(stp, TPSTATSIZE, QCMD_RD_STAT); + int stat = rdstatus((char *) stp, TPSTATSIZE, QCMD_RD_STAT); #if defined(i386) || defined(i486) - byte_swap_w(&tperror.dec); - byte_swap_w(&tperror.urc); + byte_swap_w(&(stp->dec)); + byte_swap_w(&(stp->urc)); #else /* should probably swap status bytes #definition */ #endif @@ -866,15 +942,15 @@ char vus[64]; /* vendor unique status */ int stat, i; - tpqputs("Attempting to read Extended Status 3..."); + tpqputs(TPQD_ALWAYS, "Attempting to read Extended Status 3..."); stat = rdstatus(vus, sizeof(vus), QCMD_RD_STAT_X3); if (stat != TE_OK) return stat; - tpqputs("Returned status bytes:"); + tpqputs(TPQD_ALWAYS, "Returned status bytes:"); for (i=0; i0) || dma_mode) { /* takes tooo long, shut it down */ status_dead = YES; @@ -1653,10 +1758,10 @@ status_error = YES; if (dma_mode) { dma_mode = 0; /* signal end to read/write routine */ - wake_up(&tape_qic02_transfer); + wake_up(&qic02_tape_transfer); } } -} /* tape_qic02_times_out */ +} /* qic02_tape_times_out */ /* * Interrupt handling: @@ -1679,7 +1784,7 @@ */ -/* tape_qic02_interrupt() is called when the tape controller completes +/* qic02_tape_interrupt() is called when the tape controller completes * a DMA transfer. * We are not allowed to sleep here! * @@ -1689,32 +1794,34 @@ * When we are finished, set flags to indicate end, disable timer. * NOTE: This *must* be fast! */ -static void tape_qic02_interrupt(int unused) +static void qic02_tape_interrupt(int unused) { int stat, r, i; TIMEROFF; if (status_expect_int) { +#ifdef WANT_EXTRA_FULL_DEBUGGING if (TP_DIAGS(current_tape_dev)) printk("@"); - - stat = inb(QIC_STAT_PORT); /* Knock, knock */ -#if TAPE_QIC02_IFC == ARCHIVE /* "Who's there?" */ - if (((stat & (AR_STAT_DMADONE)) == 0) && - ((stat & (QIC_STAT_EXCEPTION)) != 0)) { - TIMERCONT; - return; /* "Linux with IRQ sharing" */ - } #endif - if ((stat & QIC_STAT_EXCEPTION) == 0) { /* exception occurred */ + stat = inb(QIC02_STAT_PORT); /* Knock, knock */ + if (QIC02_TAPE_IFC == ARCHIVE) { /* "Who's there?" */ + if (((stat & (AR_STAT_DMADONE)) == 0) && + ((stat & (QIC02_STAT_EXCEPTION)) != 0)) { + TIMERCONT; + return; /* "Linux with IRQ sharing" */ + } + } + + if ((stat & QIC02_STAT_EXCEPTION) == 0) { /* exception occurred */ /* Possible causes for an exception during a transfer: * - during a write-cycle: end of tape (EW) hole detected. * - during a read-cycle: filemark or EOD detected. * - something went wrong * So don't continue with the next block. */ - tpqputs("isr: exception on tape controller"); + tpqputs(TPQD_ALWAYS, "isr: exception on tape controller"); printk(" status %02x\n", stat); status_error = TE_EX; @@ -1722,7 +1829,7 @@ dma_mode = 0; /* wake up rw() */ status_expect_int = NO; - wake_up(&tape_qic02_transfer); + wake_up(&qic02_tape_transfer); return; } /* return if tape controller not ready, or @@ -1732,15 +1839,14 @@ /* Skip next ready check for Archive controller because * it may be busy reading ahead. Weird. --hhb */ -#if TAPE_QIC02_IFC != ARCHIVE /* I think this is a drive-dependency, not IFC -- hhb */ - if (stat & QIC_STAT_READY) { /* not ready */ - tpqputs("isr: ? Tape controller not ready"); - r = 1; - } -#endif + if (QIC02_TAPE_IFC == WANGTEK) /* I think this is a drive-dependency, not IFC -- hhb */ + if (stat & QIC02_STAT_READY) { /* not ready */ + tpqputs(TPQD_ALWAYS, "isr: ? Tape controller not ready"); + r = 1; + } - if ( (i = get_dma_residue(TAPE_QIC02_DMA)) != 0 ) { - printk(TPQIC_NAME ": dma_residue == %x !!!\n", i); + if ( (i = get_dma_residue(QIC02_TAPE_DMA)) != 0 ) { + printk(TPQIC02_NAME ": dma_residue == %x !!!\n", i); r = 1; /* big trouble, but can't do much about it... */ } @@ -1756,23 +1862,23 @@ dma_mode = 0; status_expect_int = NO; TPQPUTS("isr: dma_bytes_done"); - wake_up(&tape_qic02_transfer); + wake_up(&qic02_tape_transfer); } else { /* start next transfer, account for track-switching time */ - timer_table[TAPE_QIC02_TIMER].expires = jiffies + 6*HZ; + timer_table[QIC02_TAPE_TIMER].expires = jiffies + 6*HZ; dma_transfer(); } } else { - printk(TPQIC_NAME ": Unexpected interrupt, stat == %x\n", - inb(QIC_STAT_PORT)); + printk(TPQIC02_NAME ": Unexpected interrupt, stat == %x\n", + inb(QIC02_STAT_PORT)); } -} /* tape_qic02_interrupt */ +} /* qic02_tape_interrupt */ -static int tape_qic02_lseek(struct inode * inode, struct file * file, off_t offset, int origin) +static int qic02_tape_lseek(struct inode * inode, struct file * file, off_t offset, int origin) { return -EINVAL; /* not supported */ -} /* tape_qic02_lseek */ +} /* qic02_tape_lseek */ /* read/write routines: @@ -1807,7 +1913,7 @@ * request would return the EOF flag for the previous file. */ -static int tape_qic02_read(struct inode * inode, struct file * filp, char * buf, int count) +static int qic02_tape_read(struct inode * inode, struct file * filp, char * buf, int count) { int error; dev_t dev = inode->i_rdev; @@ -1815,12 +1921,17 @@ unsigned long bytes_todo, bytes_done, total_bytes_done = 0; int stat; + if (status_zombie==YES) { + tpqputs(TPQD_ALWAYS, "configs not set"); + return -ENXIO; + } + if (TP_DIAGS(current_tape_dev)) - printk(TPQIC_NAME ": request READ, minor=%x, buf=%p, count=%x, pos=%lx, flags=%x\n", + printk(TPQIC02_NAME ": request READ, minor=%x, buf=%p, count=%x, pos=%lx, flags=%x\n", MINOR(dev), buf, count, filp->f_pos, flags); if (count % TAPE_BLKSIZE) { /* Only allow mod 512 bytes at a time. */ - tpqputs("Wrong block size"); + tpqputs(TPQD_BLKSZ, "Wrong block size"); return -EINVAL; } @@ -1832,10 +1943,8 @@ /* Make sure buffer is safe to write into. */ error = verify_area(VERIFY_WRITE, buf, count); - if (error) { - printk(TPQIC_NAME ": read: verify_area(WRITE, %p, %x) failed\n", buf, count); + if (error) return error; - } /* This is rather ugly because it has to implement a finite state * machine in order to handle the EOF situations properly. @@ -1849,7 +1958,8 @@ /* Must ensure that user program sees exactly one EOF token (==0) */ if (return_read_eof==YES) { - printk("read: return_read_eof==%d, reported_read_eof==%d, total_bytes_done==%ld\n", return_read_eof, reported_read_eof, total_bytes_done); + if (TPQDBG(DEBUG)) + printk("read: return_read_eof==%d, reported_read_eof==%d, total_bytes_done==%lu\n", return_read_eof, reported_read_eof, total_bytes_done); if (reported_read_eof==NO) { /* have not yet returned EOF to user program */ @@ -1891,7 +2001,7 @@ if (bytes_todo>0) { /* start reading data */ if (is_exception()) /****************************************/ - tpqputs("is_exception() before start_dma()!"); + tpqputs(TPQD_DMAX, "is_exception() before start_dma()!"); /****************************************************************** ***** if start_dma() fails because the head is positioned 0 bytes ***** before the FM, (causing EXCEPTION to be set) return_read_eof should @@ -1902,7 +2012,7 @@ if (stat == TE_OK) { /* Wait for transfer to complete, interrupt should wake us */ while (dma_mode != 0) { - sleep_on(&tape_qic02_transfer); + sleep_on(&qic02_tape_transfer); } if (status_error) return_read_eof = YES; @@ -1918,7 +2028,7 @@ } end_dma(&bytes_done); if (bytes_done>bytes_todo) { - tpqputs("read: Oops, read more bytes than requested"); + tpqputs(TPQD_ALWAYS, "read: Oops, read more bytes than requested"); return -EIO; } /* copy buffer to user-space in one go */ @@ -1927,7 +2037,7 @@ #if 1 /* Checks Ton's patch below */ if ((return_read_eof == NO) && (status_eof_detected == YES)) { - printk(TPQIC_NAME ": read(): return_read_eof=%d, status_eof_detected=YES. return_read_eof:=YES\n", return_read_eof); + printk(TPQIC02_NAME ": read(): return_read_eof=%d, status_eof_detected=YES. return_read_eof:=YES\n", return_read_eof); } #endif if ((bytes_todo != bytes_done) || (status_eof_detected == YES)) @@ -1943,9 +2053,9 @@ count -= bytes_done; } } - tpqputs("read request for <0 bytes"); + tpqputs(TPQD_ALWAYS, "read request for <0 bytes"); return -EINVAL; -} /* tape_qic02_read */ +} /* qic02_tape_read */ @@ -1977,24 +2087,29 @@ * tape device again. The driver will detect an exception status in (No Cartridge) * and force a rewind. After that tar may continue writing. */ -static int tape_qic02_write(struct inode * inode, struct file * filp, char * buf, int count) +static int qic02_tape_write(struct inode * inode, struct file * filp, char * buf, int count) { int error; dev_t dev = inode->i_rdev; unsigned short flags = filp->f_flags; unsigned long bytes_todo, bytes_done, total_bytes_done = 0; + if (status_zombie==YES) { + tpqputs(TPQD_ALWAYS, "configs not set"); + return -ENXIO; + } + if (TP_DIAGS(current_tape_dev)) - printk(TPQIC_NAME ": request WRITE, minor=%x, buf=%p, count=%x, pos=%lx, flags=%x\n", + printk(TPQIC02_NAME ": request WRITE, minor=%x, buf=%p, count=%x, pos=%lx, flags=%x\n", MINOR(dev), buf, count, filp->f_pos, flags); if (count % TAPE_BLKSIZE) { /* only allow mod 512 bytes at a time */ - tpqputs("Wrong block size"); + tpqputs(TPQD_BLKSZ, "Wrong block size"); return -EINVAL; } if (mode_access==READ) { - tpqputs("Not in write mode"); + tpqputs(TPQD_ALWAYS, "Not in write mode"); return -EACCES; } @@ -2003,16 +2118,14 @@ * be valid. */ if ((tperror.exs & TP_ST0) && (tperror.exs & TP_WRP)) { - tpqputs("Cartridge is write-protected."); + tpqputs(TPQD_ALWAYS, "Cartridge is write-protected."); return -EACCES; /* don't even try when write protected */ } /* Make sure buffer is safe to read from. */ error = verify_area(VERIFY_READ, buf, count); - if (error) { - printk(TPQIC_NAME ": write: verify_area(READ, %p, %x) failed\n", buf, count); + if (error) return error; - } if (doing_read == YES) terminate_read(0); @@ -2029,7 +2142,7 @@ if (reported_write_eof==NO) { if (bytes_todo>0) { - tpqputs("partial write"); + tpqputs(TPQD_ALWAYS, "partial write"); /* partial write signals EOF to user program */ } reported_write_eof = YES; @@ -2055,19 +2168,19 @@ ***** fail and write() will return ENXIO error *****/ if (start_dma(WRITE, bytes_todo) != TE_OK) { - tpqputs("write: start_dma() failed"); + tpqputs(TPQD_ALWAYS, "write: start_dma() failed"); /* should do sense() on error here */ return -ENXIO; /*********** FIXTHIS **************/ } /* Wait for write to complete, interrupt should wake us. */ while ((status_error == 0) && (dma_mode != 0)) { - sleep_on(&tape_qic02_transfer); + sleep_on(&qic02_tape_transfer); } end_dma(&bytes_done); if (bytes_done>bytes_todo) { - tpqputs("write: Oops, wrote more bytes than requested"); + tpqputs(TPQD_ALWAYS, "write: Oops, wrote more bytes than requested"); return -EIO; } /* If the dma-transfer was aborted because of an exception, @@ -2084,11 +2197,11 @@ */ if (status_error) { if (status_eom_detected == YES) { - tpqputs("write: EW detected"); + tpqputs(TPQD_ALWAYS, "write: EW detected"); return_write_eof = YES; } else { /* probably EXC_RWA */ - tpqputs("write: dma: error in writing"); + tpqputs(TPQD_ALWAYS, "write: dma: error in writing"); return -EIO; } } @@ -2106,14 +2219,15 @@ count -= bytes_done; } } - tpqputs("write request for <0 bytes"); - printk(TPQIC_NAME ": status_bytes_wr %x, buf %p, total_bytes_done %lx, count %x\n", status_bytes_wr, buf, total_bytes_done, count); + tpqputs(TPQD_ALWAYS, "write request for <0 bytes"); + if (TPQDBG(DEBUG)) + printk(TPQIC02_NAME ": status_bytes_wr %x, buf %p, total_bytes_done %lx, count %x\n", status_bytes_wr, buf, total_bytes_done, count); return -EINVAL; -} /* tape_qic02_write */ +} /* qic02_tape_write */ -/* tape_qic02_open() +/* qic02_tape_open() * We allow the device to be opened, even if it is marked 'dead' because * we want to be able to reset the tape device without rebooting. * Only one open tape file at a time, except when minor=255. @@ -2124,17 +2238,16 @@ * remembered values, rewind the tape and set the required density. * Don't rewind if the minor bits specify density 0. */ -static int tape_qic02_open(struct inode * inode, struct file * filp) +static int qic02_tape_open(struct inode * inode, struct file * filp) { dev_t dev = inode->i_rdev; unsigned short flags = filp->f_flags; - unsigned short dens; + unsigned short dens = 0; int s; - status_open = NO; if (TP_DIAGS(dev)) { - printk("tape_qic02_open: dev=%x, flags=%x ", dev, flags); + printk("qic02_tape_open: dev=%x, flags=%x ", dev, flags); } if (MINOR(dev)==255) /* special case for resetting */ @@ -2143,10 +2256,20 @@ else return -EPERM; - if (status_open==YES) { - return -EBUSY; /* only one at a time... */ + if (status_dead==YES) + /* Allow `mt reset' ioctl() even when already open()ed. */ + return 0; + + /* Only one at a time from here on... */ + if (filp->f_count>1) { /* filp->f_count==1 for the first open() */ + return -EBUSY; } - status_open = YES; + + if (status_zombie==YES) + /* no irq/dma/port stuff allocated yet, no reset done + * yet, so return until MTSETCONFIG has been done. + */ + return 0; status_bytes_rd = NO; status_bytes_wr = NO; @@ -2166,22 +2289,41 @@ mode_access = READ; break; case O_WRONLY: /* Fallthru... Strictly speaking this is not correct... */ - case O_RDWR: /* reads are allowed as long as nothing is written */ + case O_RDWR: /* Reads are allowed as long as nothing is written */ mode_access = WRITE; break; } + /* This is to avoid tape-changed problems (TP_CNI exception). + * + * Since removing the cartridge will not raise an exception, + * we always do a tp_sense() to make sure we have the proper + * CNI status, the 2150L may need an additional sense.... - Eddy + */ + s = tp_sense(TP_WRP|TP_EOM|TP_BOM|TP_CNI|TP_EOR); + + if (s == TE_OK) + /* Try to clear cartridge-changed status for Archive-2150L */ + if ((tperror.exs & TP_ST0) && (tperror.exs & TP_CNI)) + s = tp_sense(TP_WRP|TP_EOM|TP_BOM|TP_CNI|TP_EOR); - /* This is to avoid tape-changed problems (TP_CNI exception). */ - if (is_exception()) { - s = tp_sense(TP_WRP|TP_EOM|TP_BOM|TP_CNI|TP_EOR); - if (s != TE_OK) { - status_open = NO; - tpqputs("open: sense() failed after exception was detected"); - return -EIO; - } + if (s != TE_OK) { + tpqputs(TPQD_ALWAYS, "open: sense() failed"); + return -EIO; + } + + /* exception bits should be up-to-date now, so check for + * tape presence and exit if absent. + * Even `mt stat' will fail without a tape. + */ + if ((tperror.exs & TP_ST0) && (tperror.exs & TP_CNI)) { + tpqputs(TPQD_ALWAYS, "No tape present."); + return -EIO; } + /* At this point we can assume that a tape is present and + * that it will remain present until release() is called. + */ /* not allowed to do QCMD_DENS_* unless tape is rewound */ if ((TP_DENS(dev)!=0) && (TP_DENS(current_tape_dev) != TP_DENS(dev))) { @@ -2189,8 +2331,10 @@ * i.e. user wants to use tape in different format. * [assuming single drive operation] */ - tpqputs("Density minor bits have changed. Forcing rewind."); - need_rewind = YES; + if (TP_HAVE_DENS) { + tpqputs(TPQD_REWIND, "Density minor bits have changed. Forcing rewind."); + need_rewind = YES; + } } else { /* density bits still the same, but TP_DIAGS bit * may have changed. @@ -2198,30 +2342,27 @@ current_tape_dev = dev; } - if (need_rewind == YES) { + if (need_rewind == YES) { /***************** CHECK THIS!!!!!!!! **********/ s = do_qic_cmd(QCMD_REWIND, TIM_R); if (s != 0) { - tpqputs("open: rewind failed"); - status_open = NO; + tpqputs(TPQD_ALWAYS, "open: rewind failed"); return -EIO; } } /* Note: After a reset command, the controller will rewind the tape - * just before performing any tape movement operation! + * just before performing any tape movement operation! ************ SO SET need_rewind flag!!!!! */ - if (status_dead) { - tpqputs("open: tape dead, attempting reset"); + if (status_dead==YES) { + tpqputs(TPQD_ALWAYS, "open: tape dead, attempting reset"); if (tape_reset(1)!=TE_OK) { - status_open = NO; return -ENXIO; } else { status_dead = NO; if (tp_sense(~(TP_ST1|TP_ILL)) != TE_OK) { - tpqputs("open: tp_sense() failed\n"); + tpqputs(TPQD_ALWAYS, "open: tp_sense() failed\n"); status_dead = YES; /* try reset next time */ - status_open = NO; return -EIO; } } @@ -2239,14 +2380,16 @@ current_tape_dev = dev; need_rewind = NO; - dens = TP_DENS(dev); + if (TP_HAVE_DENS) + dens = TP_DENS(dev); + if (dens < sizeof(format_names)/sizeof(char *)) - printk(TPQIC_NAME ": format: %s%s\n", (dens!=0)? "QIC-" : "", format_names[dens]); + printk(TPQIC02_NAME ": format: %s%s\n", (dens!=0)? "QIC-" : "", format_names[dens]); else - tpqputs("Wait for retensioning..."); + tpqputs(TPQD_REWIND, "Wait for retensioning..."); switch (TP_DENS(dev)) { - case 0: /* This one's for Eddy ;-) */ + case 0: /* Minor 0 is for drives without set-density support */ s = 0; break; case 1: @@ -2271,28 +2414,32 @@ s = do_qic_cmd(QCMD_RETEN, TIM_R); } if (s != 0) { - status_open = NO; /* fail if fault occurred */ status_dead = YES; /* force reset */ current_tape_dev = 0xff80; return -EIO; } return 0; -} /* tape_qic02_open */ +} /* qic02_tape_open */ -static int tape_qic02_readdir(struct inode * inode, struct file * filp, struct dirent * dp, int count) + +static int qic02_tape_readdir(struct inode * inode, struct file * filp, struct dirent * dp, int count) { return -ENOTDIR; /* not supported */ -} /* tape_qic02_readdir */ +} /* qic02_tape_readdir */ + -static void tape_qic02_release(struct inode * inode, struct file * filp) +static void qic02_tape_release(struct inode * inode, struct file * filp) { dev_t dev = inode->i_rdev; if (TP_DIAGS(dev)) - printk("tape_qic02_release: dev=%x\n", dev); + printk("qic02_tape_release: dev=%x\n", dev); + + if (status_zombie==YES) /* don't rewind in zombie mode */ + return; /* Terminate any pending write cycle. Terminating the read-cycle * is delayed until it is required to do so for a new command. @@ -2300,28 +2447,112 @@ terminate_write(-1); if (status_dead==YES) - tpqputs("release: device dead!?"); + tpqputs(TPQD_ALWAYS, "release: device dead!?"); - if (status_open==NO) { - tpqputs("release: device not open"); - return; - } /* Rewind only if minor number requires it AND - * read/writes have been done. + * read/writes have been done. ************* IS THIS CORRECT?????????? */ if ((TP_REWCLOSE(dev)) && (status_bytes_rd | status_bytes_wr)) { - tpqputs("release: Doing rewind..."); + tpqputs(TPQD_REWIND, "release: Doing rewind..."); (void) do_qic_cmd(QCMD_REWIND, TIM_R); } - status_open = NO; return; -} /* tape_qic02_release */ +} /* qic02_tape_release */ + +#ifdef CONFIG_QIC02_DYNCONF +/* Set masks etc. based on the interface card type. */ +int update_ifc_masks(int ifc) +{ + QIC02_TAPE_IFC = ifc; + + if ((QIC02_TAPE_IFC == WANGTEK) || (QIC02_TAPE_IFC == EVEREX)) { + QIC02_STAT_PORT = QIC02_TAPE_PORT; + QIC02_CTL_PORT = QIC02_TAPE_PORT; + QIC02_CMD_PORT = QIC02_TAPE_PORT+1; + QIC02_DATA_PORT = QIC02_TAPE_PORT+1; + QIC02_STAT_READY = WT_QIC02_STAT_READY; + QIC02_STAT_EXCEPTION = WT_QIC02_STAT_EXCEPTION; + QIC02_STAT_MASK = WT_QIC02_STAT_MASK; + + QIC02_STAT_RESETMASK = WT_QIC02_STAT_RESETMASK; + QIC02_STAT_RESETVAL = WT_QIC02_STAT_RESETVAL; + + QIC02_CTL_RESET = WT_QIC02_CTL_RESET; + QIC02_CTL_REQUEST = WT_QIC02_CTL_REQUEST; + + if (QIC02_TAPE_DMA == 3) + WT_CTL_DMA = WT_CTL_DMA3; + else if (QIC02_TAPE_DMA == 1) + WT_CTL_DMA = WT_CTL_DMA1; + else { + tpqputs(TPQD_ALWAYS, "Unsupported or incorrect DMA channel"); + return -EIO; + } + + if (QIC02_TAPE_IFC == EVEREX) { + /* Everex is a special case for Wangtek (actually + * it's the other way 'round, but I saw Wangtek first) + */ + if (QIC02_TAPE_DMA==3) + WT_CTL_DMA = WT_CTL_DMA1; + /* Fixup the kernel copy of the IFC type to that + * we don't have to distinguish between Wangtek and + * and Everex at runtime. + */ + QIC02_TAPE_IFC = WANGTEK; + } + } else if (QIC02_TAPE_IFC == ARCHIVE) { + QIC02_STAT_PORT = QIC02_TAPE_PORT+1; + QIC02_CTL_PORT = QIC02_TAPE_PORT+1; + QIC02_CMD_PORT = QIC02_TAPE_PORT; + QIC02_DATA_PORT = QIC02_TAPE_PORT; + QIC02_STAT_READY = AR_QIC02_STAT_READY; + QIC02_STAT_EXCEPTION = AR_QIC02_STAT_EXCEPTION; + QIC02_STAT_MASK = AR_QIC02_STAT_MASK; + + QIC02_STAT_RESETMASK = AR_QIC02_STAT_RESETMASK; + QIC02_STAT_RESETVAL = AR_QIC02_STAT_RESETVAL; + + QIC02_CTL_RESET = AR_QIC02_CTL_RESET; + QIC02_CTL_REQUEST = AR_QIC02_CTL_REQUEST; + + if (QIC02_TAPE_DMA > 3) { + tpqputs(TPQD_ALWAYS, "Unsupported or incorrect DMA channel"); + return -EIO; + } + } else if (QIC02_TAPE_IFC == MOUNTAIN) { + QIC02_STAT_PORT = QIC02_TAPE_PORT+1; + QIC02_CTL_PORT = QIC02_TAPE_PORT+1; + QIC02_CMD_PORT = QIC02_TAPE_PORT; + QIC02_DATA_PORT = QIC02_TAPE_PORT; + + QIC02_STAT_READY = MTN_QIC02_STAT_READY; + QIC02_STAT_EXCEPTION = MTN_QIC02_STAT_EXCEPTION; + QIC02_STAT_MASK = MTN_QIC02_STAT_MASK; + + QIC02_STAT_RESETMASK = MTN_QIC02_STAT_RESETMASK; + QIC02_STAT_RESETVAL = MTN_QIC02_STAT_RESETVAL; + + QIC02_CTL_RESET = MTN_QIC02_CTL_RESET; + QIC02_CTL_REQUEST = MTN_QIC02_CTL_REQUEST; + + if (QIC02_TAPE_DMA > 3) { + tpqputs(TPQD_ALWAYS, "Unsupported or incorrect DMA channel"); + return -EIO; + } + } else { + tpqputs(TPQD_ALWAYS, "Invalid interface type"); + return -ENXIO; + } + return 0; +} /* update_ifc-masks */ +#endif /* ioctl allows user programs to rewind the tape and stuff like that */ -static int tape_qic02_ioctl(struct inode * inode, struct file * filp, +static int qic02_tape_ioctl(struct inode * inode, struct file * filp, unsigned int iocmd, unsigned long ioarg) { int error; @@ -2330,14 +2561,12 @@ int c; struct mtop operation; char *stp, *argp; - -#ifdef TP_HAVE_TELL unsigned char blk_addr[6]; struct mtpos ioctl_tell; -#endif + if (TP_DIAGS(current_tape_dev)) - printk(TPQIC_NAME ": ioctl(%4x, %4x, %4lx)\n", dev_maj, iocmd, ioarg); + printk(TPQIC02_NAME ": ioctl(%4x, %4x, %4lx)\n", dev_maj, iocmd, ioarg); if (!inode || !ioarg) return -EINVAL; @@ -2345,19 +2574,100 @@ /* check iocmd first */ if (dev_maj != QIC02_TAPE_MAJOR) { - printk(TPQIC_NAME ": Oops! Wrong device?\n"); + printk(TPQIC02_NAME ": Oops! Wrong device?\n"); /* A panic() would be appropriate here */ return -ENODEV; } c = iocmd & IOCCMD_MASK; + +#ifdef DDIOCSDBG + /* Check for DDI Debug Control, contributed by FvK, edited by HHB. */ + if (c == DDIOCSDBG) { + if (!suser()) + return -EPERM; + verify_area(VERIFY_READ, (int *) ioarg, sizeof(int)); + c = get_fs_long((int *) ioarg); + if (c==0) { + QIC02_TAPE_DEBUG = 0; + return 0; + } + if ((c>=1) && (c<=32)) { + QIC02_TAPE_DEBUG |= (1 << (c-1)); + return 0; + } + if (c >= 128) { + QIC02_TAPE_DEBUG &= ~(1 << (c - 128)); + return 0; + } + return -EINVAL; + } +#endif + +#ifdef CONFIG_QIC02_DYNCONF + if (c == (MTIOCGETCONFIG & IOCCMD_MASK)) { + if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtconfiginfo)) { + tpqputs(TPQD_ALWAYS, "sizeof(struct mtconfiginfo) does not match!"); + return -EFAULT; + } + + /* check for valid user address */ + error = verify_area(VERIFY_WRITE, (void *) ioarg, sizeof(qic02_tape_dynconf)); + if (error) + return error; + /* copy current settings to user space */ + stp = (char *) &qic02_tape_dynconf; + argp = (char *) ioarg; + for (i=0; i> IOCSIZE_SHIFT) != sizeof(struct mtconfiginfo)) { + tpqputs(TPQD_ALWAYS, "sizeof(struct mtconfiginfo) does not match!"); + return -EFAULT; + } + if (!suser()) + return -EPERM; + if ((doing_read!=NO) || (doing_write!=NO)) + return -EBUSY; + error = verify_area(VERIFY_READ, (char *) ioarg, sizeof(qic02_tape_dynconf)); + if (error) + return error; + + /* copy struct from user space to kernel space */ + stp = (char *) &qic02_tape_dynconf; + argp = (char *) ioarg; + for (i=0; i> IOCSIZE_SHIFT) != sizeof(struct mtop)) { - tpqputs("sizeof(struct mtop) does not match!"); + tpqputs(TPQD_ALWAYS, "sizeof(struct mtop) does not match!"); return -EFAULT; } error = verify_area(VERIFY_READ, (char *) ioarg, sizeof(operation)); @@ -2382,16 +2692,17 @@ printk("OP op=%4x, count=%4x\n", operation.mt_op, operation.mt_count); if (operation.mt_count < 0) - tpqputs("Warning: negative mt_count ignored"); + tpqputs(TPQD_ALWAYS, "Warning: negative mt_count ignored"); ioctl_status.mt_resid = operation.mt_count; if (operation.mt_op == MTSEEK) { + if (!TP_HAVE_SEEK) + return -ENOTTY; seek_addr_buf[0] = (operation.mt_count>>16)&0xff; seek_addr_buf[1] = (operation.mt_count>>8)&0xff; seek_addr_buf[2] = (operation.mt_count)&0xff; if (operation.mt_count>>24) return -EINVAL; - if ((error = do_ioctl_cmd(operation.mt_op)) != 0) return error; ioctl_status.mt_resid = 0; @@ -2411,7 +2722,7 @@ /* compare expected struct size and actual struct size */ if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtget)) { - tpqputs("sizeof(struct mtget) does not match!"); + tpqputs(TPQD_ALWAYS, "sizeof(struct mtget) does not match!"); return -EFAULT; } @@ -2432,14 +2743,14 @@ put_fs_byte(*stp++, argp++); return 0; -#ifdef TP_HAVE_TELL - } else if (c == (MTIOCPOS & IOCCMD_MASK)) { + + } else if (TP_HAVE_TELL && (c == (MTIOCPOS & IOCCMD_MASK))) { if (TP_DIAGS(current_tape_dev)) printk("POS "); /* compare expected struct size and actual struct size */ if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtpos)) { - tpqputs("sizeof(struct mtpos) does not match!"); + tpqputs(TPQD_ALWAYS, "sizeof(struct mtpos) does not match!"); return -EFAULT; } @@ -2448,11 +2759,11 @@ if (error) return error; - tpqputs("MTTELL reading block address"); + tpqputs(TPQD_IOCTLS, "MTTELL reading block address"); if ((doing_read==YES) || (doing_write==YES)) - finish_rw(QCMDV_TELL_BLK); + finish_rw(AR_QCMDV_TELL_BLK); - c = rdstatus((char *) blk_addr, sizeof(blk_addr), QCMDV_TELL_BLK); + c = rdstatus((char *) blk_addr, sizeof(blk_addr), AR_QCMDV_TELL_BLK); if (c!=TE_OK) return -EIO; @@ -2464,24 +2775,24 @@ for (i=0; i16) { + tpqputs(TPQD_ALWAYS, "Bogus interrupt number."); + return -1; + } - /* Should do IRQ/DMA allocation in open(). Use free_irq() in release() - * return -EBUSY, if allocation fails in open(). - * Make IRQ settable/readable through ioctls. DMA is trickier because - * some bits change for different DMA numbers. Also, this would add - * runtime overhead for having the dma channel number be a variable - * rather than a constant. Probably need to make the DMA stuff #ifdef'd - * to choose between hardcoded and changeable DMA channel. - * - * Also, Linux needs a more generic way to set DMA/IRQ and other stuff. - * Right now, this is done differently for every device. (setserial, - * ctrlaltdel, setdfprm, setfdthr, tunelp, ((setterm))...) - * - * #include + /* for DYNCONF, allocating DMA & IRQ should not be done until + * the config parameters have been set using MTSETCONFIG. */ /* get IRQ */ - if (irqaction(TAPE_QIC02_IRQ, &tape_qic02_sigaction)) { - printk(TPQIC_NAME ": can't allocate IRQ%d for QIC-02 tape\n", - TAPE_QIC02_IRQ); - return kmem_start; + if (irqaction(QIC02_TAPE_IRQ, &qic02_tape_sigaction)) { + printk(TPQIC02_NAME ": can't allocate IRQ%d for QIC-02 tape\n", + QIC02_TAPE_IRQ); + status_zombie = YES; + return -1; } /* After IRQ, allocate DMA channel */ - if (request_dma(TAPE_QIC02_DMA)) { - printk(TPQIC_NAME ": can't allocate DMA%d for QIC-02 tape\n", - TAPE_QIC02_DMA); - free_irq(TAPE_QIC02_IRQ); - return kmem_start; + if (request_dma(QIC02_TAPE_DMA)) { + printk(TPQIC02_NAME ": can't allocate DMA%d for QIC-02 tape\n", + QIC02_TAPE_DMA); + free_irq(QIC02_TAPE_IRQ); + status_zombie = YES; + return -1; + } + + printk(TPQIC02_NAME ": Settings: IRQ %d, DMA %d, IO 0x%x, IFC %s\n", + QIC02_TAPE_IRQ, QIC02_TAPE_DMA, + ((QIC02_TAPE_IFC==ARCHIVE) || (QIC02_TAPE_IFC==MOUNTAIN))? + QIC02_CMD_PORT : QIC02_STAT_PORT, + (QIC02_TAPE_IFC==MOUNTAIN)? "Mountain" : + ((QIC02_TAPE_IFC==ARCHIVE)? "Archive" : "Wangtek")); + + if (tape_reset(0)!=TE_OK || tp_sense(TP_WRP|TP_POR|TP_CNI)!=TE_OK) { + /* No drive detected, so vanish */ + tpqputs(TPQD_ALWAYS, "No drive detected -- releasing irq and dma."); + status_dead = YES; + qic02_release_resources(); + return -1; } + /* All should be ok now */ + status_zombie = NO; + return 0; +} /* qic02_get_resources */ + + + +/* init() is called from chr_dev_init() in drivers/char/mem.c */ +long qic02_tape_init(long kmem_start) + /* Shouldn't this be a caddr_t ? */ +{ + if (TPSTATSIZE != 6) { - printk(TPQIC_NAME ": internal error: tpstatus struct incorrect!\n"); + printk(TPQIC02_NAME ": internal error: tpstatus struct incorrect!\n"); return kmem_start; } if ((TPQBUF_SIZE<512) || (TPQBUF_SIZE>=0x10000)) { - printk(TPQIC_NAME ": internal error: DMA buffer size out of range\n"); + printk(TPQIC02_NAME ": internal error: DMA buffer size out of range\n"); return kmem_start; } - printk(TPQIC_NAME ": DMA buffers: %u blocks", NR_BLK_BUF); + QIC02_TAPE_DEBUG = TPQD_DEFAULT_FLAGS; + +#ifndef CONFIG_QIC02_DYNCONF + printk(TPQIC02_NAME ": IRQ %d, DMA %d, IO 0x%x, IFC %s, %s, %s\n", + QIC02_TAPE_IRQ, QIC02_TAPE_DMA, +# if QIC02_TAPE_IFC == WANGTEK + QIC02_STAT_PORT, "Wangtek", +# elif QIC02_TAPE_IFC == ARCHIVE + QIC02_CMD_PORT, "Archive", +# elif QIC02_TAPE_IFC == MOUNTAIN + QIC02_CMD_PORT, "Mountain", +# else +# error +# endif + rcs_revision, rcs_date); + if (qic02_get_resources()) + return kmem_start; +#else + printk(TPQIC02_NAME ": Runtime config, %s, %s\n", + rcs_revision, rcs_date); + + QIC02_TAPE_IRQ = BOGUS_IRQ; /* invalid value */ +#endif + + printk(TPQIC02_NAME ": DMA buffers: %u blocks", NR_BLK_BUF); /* Setup the page-address for the dma transfer. * This assumes a one-to-one identity mapping between * kernel addresses and physical memory. */ - buffaddr = align_buffer((unsigned long) &tape_qic02_buf, TAPE_BLKSIZE); - printk(", at address 0x%lx (0x%lx)\n", buffaddr, (unsigned long) &tape_qic02_buf); + buffaddr = align_buffer((unsigned long) &qic02_tape_buf, TAPE_BLKSIZE); + printk(", at address 0x%lx (0x%lx)\n", buffaddr, (unsigned long) &qic02_tape_buf); #ifndef CONFIG_MAX_16M if (buffaddr+TPQBUF_SIZE>=0x1000000) { - printk(TPQIC_NAME ": DMA buffer *must* be in lower 16MB\n"); + printk(TPQIC02_NAME ": DMA buffer *must* be in lower 16MB\n"); return kmem_start; } #endif /* If we got this far, install driver functions */ - if (register_chrdev(QIC02_TAPE_MAJOR, TPQIC_NAME, &tape_qic02_fops)) { - printk(TPQIC_NAME ": Unable to get chrdev major %d\n", - QIC02_TAPE_MAJOR); + if (register_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME, &qic02_tape_fops)) { + printk(TPQIC02_NAME ": Unable to get chrdev major %d\n", QIC02_TAPE_MAJOR); +#ifndef CONFIG_QIC02_DYNCONF + free_irq(QIC02_TAPE_IRQ); + free_dma(QIC02_TAPE_DMA); +#endif return kmem_start; } /* prepare timer */ TIMEROFF; - timer_table[TAPE_QIC02_TIMER].expires = 0; - timer_table[TAPE_QIC02_TIMER].fn = tape_qic02_times_out; + timer_table[QIC02_TAPE_TIMER].expires = 0; + timer_table[QIC02_TAPE_TIMER].fn = qic02_tape_times_out; +#ifndef CONFIG_QIC02_DYNCONF if (tape_reset(0)!=TE_OK || tp_sense(TP_WRP|TP_POR|TP_CNI)!=TE_OK) { + /* No drive detected, so vanish */ + tpqputs(TPQD_ALWAYS, "No drive detected -- driver going on vacation..."); status_dead = YES; + free_irq(QIC02_TAPE_IRQ); + free_dma(QIC02_TAPE_DMA); + unregister_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME); + return kmem_start; } else { if (is_exception()) { - tpqputs("exception detected\n"); + tpqputs(TPQD_ALWAYS, "exception detected\n"); (void) tp_sense(TP_WRP|TP_POR|TP_CNI); } } +#endif /* initialize generic status for ioctl requests */ - ioctl_status.mt_type = TAPE_QIC02_DRIVE; /* MT_IS* id nr */ + ioctl_status.mt_type = QIC02_TAPE_DRIVE; /* MT_IS* id nr */ ioctl_status.mt_resid = 0; /* ---residual count */ ioctl_status.mt_gstat = 0; /* ---generic status */ @@ -2616,7 +2981,7 @@ ioctl_status.mt_blkno = 0; /* number of current (logical) block */ return kmem_start; -} /* tape_qic02_init */ +} /* qic02_tape_init */ -#endif /* CONFIG_TAPE_QIC02 */ +#endif /* CONFIG_QIC02_TAPE */ diff -u --recursive --new-file v1.1.34/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c --- v1.1.34/linux/drivers/char/tty_io.c Fri Jul 22 16:41:01 1994 +++ linux/drivers/char/tty_io.c Sun Jul 24 17:39:43 1994 @@ -69,7 +69,7 @@ extern int set_selection(const int arg, struct tty_struct *tty); extern int paste_selection(struct tty_struct *tty); extern int sel_loadlut(const int arg); -extern int mouse_reporting_p(void); +extern int mouse_reporting(void); extern int shift_state; #endif /* CONFIG_SELECTION */ extern int do_screendump(int arg); @@ -1394,7 +1394,8 @@ put_fs_byte(shift_state,arg); return 0; case 7: - return mouse_reporting_p(); + put_fs_byte(mouse_reporting(),arg); + return 0; #endif /* CONFIG_SELECTION */ default: return -EINVAL; diff -u --recursive --new-file v1.1.34/linux/drivers/net/auto_irq.c linux/drivers/net/auto_irq.c --- v1.1.34/linux/drivers/net/auto_irq.c Tue Apr 19 10:53:43 1994 +++ linux/drivers/net/auto_irq.c Sun Jul 24 17:44:45 1994 @@ -62,8 +62,6 @@ int i, mask; int timeout = jiffies+waittime; - irq_number = 0; - irq_bitmap = 0; irq_handled = 0; for (i = 0; i < 16; i++) { if (!irqaction(i, &autoirq_sigaction)) @@ -71,6 +69,8 @@ } /* Update our USED lists. */ irqs_used |= ~irq_handled; + irq_number = 0; + irq_bitmap = 0; /* Hang out at least jiffies waiting for bogus IRQ hits. */ while (timeout > jiffies) diff -u --recursive --new-file v1.1.34/linux/drivers/scsi/ultrastor.c linux/drivers/scsi/ultrastor.c --- v1.1.34/linux/drivers/scsi/ultrastor.c Thu Jul 14 00:48:25 1994 +++ linux/drivers/scsi/ultrastor.c Sat Jul 23 13:26:24 1994 @@ -305,7 +305,7 @@ volatile unsigned char *mem) { asm("xchgb %0,%1" : - "=r" (reg), "=m" (*(unsigned char *)mem) : + "=q" (reg), "=m" (*(unsigned char *)mem) : "0" (reg), "1" (*(unsigned char *)mem)); return reg; } diff -u --recursive --new-file v1.1.34/linux/fs/Makefile linux/fs/Makefile --- v1.1.34/linux/fs/Makefile Tue May 24 08:47:06 1994 +++ linux/fs/Makefile Sun Jul 24 17:39:43 1994 @@ -33,6 +33,9 @@ ifdef CONFIG_XIA_FS FS_SUBDIRS := $(FS_SUBDIRS) xiafs endif +ifdef CONFIG_UMSDOS_FS +FS_SUBDIRS := $(FS_SUBDIRS) umsdos +endif ifdef CONFIG_SYSV_FS FS_SUBDIRS := $(FS_SUBDIRS) sysv endif diff -u --recursive --new-file v1.1.34/linux/fs/filesystems.c linux/fs/filesystems.c --- v1.1.34/linux/fs/filesystems.c Mon Apr 25 10:04:33 1994 +++ linux/fs/filesystems.c Sun Jul 24 17:39:44 1994 @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -51,6 +52,10 @@ #ifdef CONFIG_XIA_FS register_filesystem(&(struct file_system_type) {xiafs_read_super, "xiafs", 1, NULL}); +#endif +#ifdef CONFIG_UMSDOS_FS + register_filesystem(&(struct file_system_type) + {UMSDOS_read_super, "umsdos", 1, NULL}); #endif #ifdef CONFIG_MSDOS_FS diff -u --recursive --new-file v1.1.34/linux/fs/msdos/Makefile linux/fs/msdos/Makefile --- v1.1.34/linux/fs/msdos/Makefile Wed Dec 1 14:44:15 1993 +++ linux/fs/msdos/Makefile Sun Jul 24 17:39:44 1994 @@ -14,7 +14,7 @@ .s.o: $(AS) -o $*.o $< -OBJS= namei.o inode.o file.o dir.o misc.o fat.o +OBJS= namei.o inode.o file.o dir.o misc.o fat.o mmap.o msdos.o: $(OBJS) $(LD) -r -o msdos.o $(OBJS) diff -u --recursive --new-file v1.1.34/linux/fs/msdos/dir.c linux/fs/msdos/dir.c --- v1.1.34/linux/fs/msdos/dir.c Wed Dec 1 14:44:15 1993 +++ linux/fs/msdos/dir.c Sun Jul 24 17:39:44 1994 @@ -12,6 +12,7 @@ #include #include #include +#include static int msdos_dir_read(struct inode * inode,struct file * filp, char * buf,int count) @@ -19,7 +20,7 @@ return -EISDIR; } -static int msdos_readdir(struct inode *inode,struct file *filp, +int msdos_readdir(struct inode *inode,struct file *filp, struct dirent *dirent,int count); @@ -54,8 +55,11 @@ NULL /* permission */ }; -static int msdos_readdir(struct inode *inode,struct file *filp, - struct dirent *dirent,int count) +int msdos_readdir( + struct inode *inode, + struct file *filp, + struct dirent *dirent, /* dirent in user space */ + int count) { int ino,i,i2,last; char c,*walk; @@ -67,33 +71,39 @@ /* Fake . and .. for the root directory. */ if (filp->f_pos == 2) filp->f_pos = 0; else if (filp->f_pos < 2) { - walk = filp->f_pos++ ? ".." : "."; - for (i = 0; *walk; walk++) - put_fs_byte(*walk,dirent->d_name+i++); - put_fs_long(MSDOS_ROOT_INO,&dirent->d_ino); - put_fs_byte(0,dirent->d_name+i); - put_fs_word(i,&dirent->d_reclen); - return i; - } + walk = filp->f_pos++ ? ".." : "."; + for (i = 0; *walk; walk++) + put_fs_byte(*walk,dirent->d_name+i++); + put_fs_long(MSDOS_ROOT_INO,&dirent->d_ino); + put_fs_byte(0,dirent->d_name+i); + put_fs_word(i,&dirent->d_reclen); + return i; + } } if (filp->f_pos & (sizeof(struct msdos_dir_entry)-1)) return -ENOENT; bh = NULL; while ((ino = msdos_get_entry(inode,&filp->f_pos,&bh,&de)) > -1) { if (!IS_FREE(de->name) && !(de->attr & ATTR_VOLUME)) { + char bufname[13]; + char *ptname = bufname; for (i = last = 0; i < 8; i++) { if (!(c = de->name[i])) break; if (c >= 'A' && c <= 'Z') c += 32; - if (c != ' ') last = i+1; - put_fs_byte(c,i+dirent->d_name); + if (c != ' '){ + last = i+1; + *ptname++ = c; + } } i = last; - put_fs_byte('.',i+dirent->d_name); + *ptname++ = '.'; i++; for (i2 = 0; i2 < 3; i2++) { if (!(c = de->ext[i2])) break; if (c >= 'A' && c <= 'Z') c += 32; - if (c != ' ') last = i+1; - put_fs_byte(c,i+dirent->d_name); + if (c != ' '){ + last = i+1; + *ptname++ = c; + } i++; } if ((i = last) != 0) { @@ -101,8 +111,9 @@ ino = inode->i_ino; else if (!strcmp(de->name,MSDOS_DOTDOT)) ino = msdos_parent_ino(inode,0); + bufname[i] = '\0'; put_fs_long(ino,&dirent->d_ino); - put_fs_byte(0,i+dirent->d_name); + memcpy_tofs(dirent->d_name,bufname,i+1); put_fs_word(i,&dirent->d_reclen); brelse(bh); return i; diff -u --recursive --new-file v1.1.34/linux/fs/msdos/file.c linux/fs/msdos/file.c --- v1.1.34/linux/fs/msdos/file.c Tue Apr 19 10:53:24 1994 +++ linux/fs/msdos/file.c Sun Jul 24 17:39:44 1994 @@ -15,16 +15,11 @@ #include #include #include +#include #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #define MAX(a,b) (((a) > (b)) ? (a) : (b)) -static int msdos_file_read(struct inode *inode,struct file *filp,char *buf, - int count); -static int msdos_file_write(struct inode *inode,struct file *filp,char *buf, - int count); - - static struct file_operations msdos_file_operations = { NULL, /* lseek - default */ msdos_file_read, /* read */ @@ -32,7 +27,7 @@ NULL, /* readdir - bad */ NULL, /* select - default */ NULL, /* ioctl - default */ - NULL, /* mmap */ + msdos_mmap, /* mmap */ NULL, /* no special open is needed */ NULL, /* release */ file_fsync /* fsync */ @@ -53,7 +48,8 @@ NULL, /* follow_link */ msdos_bmap, /* bmap */ msdos_truncate, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + msdos_smap /* smap */ }; /* No bmap for MS-DOS FS' that don't align data at kByte boundaries. */ @@ -73,12 +69,19 @@ NULL, /* follow_link */ NULL, /* bmap */ msdos_truncate, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + msdos_smap /* smap */ }; -static int msdos_file_read(struct inode *inode,struct file *filp,char *buf, - int count) +/* + Read a file into user space +*/ +int msdos_file_read( + struct inode *inode, + struct file *filp, + char *buf, + int count) { char *start; int left,offset,size,sector,cnt; @@ -91,7 +94,8 @@ printk("msdos_file_read: inode = NULL\n"); return -EINVAL; } - if (!S_ISREG(inode->i_mode)) { + /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */ + if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) { printk("msdos_file_read: mode = %07o\n",inode->i_mode); return -EINVAL; } @@ -131,9 +135,14 @@ return buf-start; } - -static int msdos_file_write(struct inode *inode,struct file *filp,char *buf, - int count) +/* + Write to a file either from user space +*/ +int msdos_file_write( + struct inode *inode, + struct file *filp, + char *buf, + int count) { int sector,offset,size,left,written; int error,carry; @@ -145,7 +154,8 @@ printk("msdos_file_write: inode = NULL\n"); return -EINVAL; } - if (!S_ISREG(inode->i_mode)) { + /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */ + if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) { printk("msdos_file_write: mode = %07o\n",inode->i_mode); return -EINVAL; } @@ -210,7 +220,6 @@ inode->i_dirt = 1; return buf-start; } - void msdos_truncate(struct inode *inode) { diff -u --recursive --new-file v1.1.34/linux/fs/msdos/mmap.c linux/fs/msdos/mmap.c --- v1.1.34/linux/fs/msdos/mmap.c Thu Jan 1 02:00:00 1970 +++ linux/fs/msdos/mmap.c Sun Jul 24 17:39:44 1994 @@ -0,0 +1,123 @@ +/* + * fs/msdos/mmap.c + * + * Written by Jacques Gelinas (jacques@solucorp.qc.ca) + * Inspired by fs/nfs/mmap.c (Jaon Tombs 15 Aug 1993) + * + * msdos mmap handling + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Fill in the supplied page for mmap + */ +static unsigned long msdos_file_mmap_nopage( + struct vm_area_struct * area, + unsigned long address, + unsigned long page, + int error_code) +{ + struct inode * inode = area->vm_inode; + unsigned int clear; + int pos; + long gap; /* distance from eof to pos */ + + address &= PAGE_MASK; + pos = address - area->vm_start + area->vm_offset; + + clear = 0; + gap = inode->i_size - pos; + if (gap <= 0){ + /* mmaping beyong end of file */ + clear = PAGE_SIZE; + }else{ + int cur_read; + int need_read; + struct file filp; + if (gap < PAGE_SIZE){ + clear = PAGE_SIZE - gap; + } + filp.f_pos = pos; + need_read = PAGE_SIZE - clear; + { + unsigned long cur_fs = get_fs(); + set_fs (KERNEL_DS); + cur_read = msdos_file_read (inode,&filp,(char*)page + ,need_read); + set_fs (cur_fs); + } + if (cur_read != need_read){ + printk ("MSDOS: Error while reading an mmap file %d <> %d\n" + ,cur_read,need_read); + } + } + if (clear > 0){ + memset ((char*)page+PAGE_SIZE-clear,0,clear); + } + return page; +} + +struct vm_operations_struct msdos_file_mmap = { + NULL, /* open */ + NULL, /* close */ + msdos_file_mmap_nopage, /* nopage */ + NULL, /* wppage */ + NULL, /* share */ + NULL, /* unmap */ +}; + +/* + * This is used for a general mmap of an msdos file + * Returns 0 if ok, or a negative error code if not. + */ +int msdos_mmap( + struct inode * inode, + struct file * file, + unsigned long addr, + size_t len, + int prot, + unsigned long off) +{ + struct vm_area_struct * mpnt; + + if (prot & PAGE_RW) /* only PAGE_COW or read-only supported now */ + return -EINVAL; + if (off & (inode->i_sb->s_blocksize - 1)) + return -EINVAL; + if (!inode->i_sb || !S_ISREG(inode->i_mode)) + return -EACCES; + if (!IS_RDONLY(inode)) { + inode->i_atime = CURRENT_TIME; + inode->i_dirt = 1; + } + + mpnt = (struct vm_area_struct * ) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL); + if (!mpnt) + return -ENOMEM; + + unmap_page_range(addr, len); + mpnt->vm_task = current; + mpnt->vm_start = addr; + mpnt->vm_end = addr + len; + mpnt->vm_page_prot = prot; + mpnt->vm_share = NULL; + mpnt->vm_inode = inode; + inode->i_count++; + mpnt->vm_offset = off; + mpnt->vm_ops = &msdos_file_mmap; + insert_vm_struct (current,mpnt); + merge_segments (current->mm->mmap,NULL,NULL); + return 0; +} + diff -u --recursive --new-file v1.1.34/linux/fs/msdos/namei.c linux/fs/msdos/namei.c --- v1.1.34/linux/fs/msdos/namei.c Tue Apr 19 10:53:24 1994 +++ linux/fs/msdos/namei.c Sun Jul 24 17:39:44 1994 @@ -360,7 +360,11 @@ } -int msdos_unlink(struct inode *dir,const char *name,int len) +static int msdos_unlinkx( + struct inode *dir, + const char *name, + int len, + int nospc) /* Flag special file ? */ { int res,ino; struct buffer_head *bh; @@ -375,7 +379,7 @@ res = -ENOENT; goto unlink_done; } - if (!S_ISREG(inode->i_mode)) { + if (!S_ISREG(inode->i_mode) && nospc){ res = -EPERM; goto unlink_done; } @@ -392,6 +396,17 @@ return res; } +int msdos_unlink(struct inode *dir,const char *name,int len) +{ + return msdos_unlinkx (dir,name,len,1); +} +/* + Special entry for umsdos +*/ +int msdos_unlink_umsdos(struct inode *dir,const char *name,int len) +{ + return msdos_unlinkx (dir,name,len,0); +} static int rename_same_dir(struct inode *old_dir,char *old_name, struct inode *new_dir,char *new_name,struct buffer_head *old_bh, diff -u --recursive --new-file v1.1.34/linux/fs/umsdos/Makefile linux/fs/umsdos/Makefile --- v1.1.34/linux/fs/umsdos/Makefile Thu Jan 1 02:00:00 1970 +++ linux/fs/umsdos/Makefile Sun Jul 24 17:39:44 1994 @@ -0,0 +1,40 @@ +# +# Makefile for the umsdos unix-like filesystem routines. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +.c.s: + $(CC) $(CFLAGS) -S $< +.c.o: + $(CC) $(CFLAGS) -c $< +.s.o: + $(AS) -o $*.o $< + +OBJS= dir.o emd.o file.o inode.o ioctl.o mangle.o namei.o\ + rdir.o symlink.o #check.o + +umsdos.o: $(OBJS) + $(LD) -r -o umsdos.o $(OBJS) + +clean: + rm -f core *.o *.a *.s + +dep: + $(CPP) -M *.c > .depend + +p: + proto *.c >/usr/include/linux/umsdos_fs.p + +doc: + nadoc -i -p umsdos.doc - /tmp/umsdos.mpg + +# +# include a dependency file if one exists +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff -u --recursive --new-file v1.1.34/linux/fs/umsdos/README linux/fs/umsdos/README --- v1.1.34/linux/fs/umsdos/README Thu Jan 1 02:00:00 1970 +++ linux/fs/umsdos/README Sun Jul 24 17:39:44 1994 @@ -0,0 +1,84 @@ +Very short explanation for the impatient!!! + +Umsdos is a file system driver that run on top the MSDOS fs driver. +It is written by Jacques Gelinas (jacques@solucorp.qc.ca) + +Umsdos is not a file system per se, but a twist to make a boring +one into a useful one. + +It gives you: + + long file name + Permisssions and owner + Links + Special files (devices, pipe...) + All is need to be a linux root fs. + +There is plenty of documentation on it in the source. A formated document +made from those comments is available from +sunsite.unc.edu:/pub/Linux/ALPHA/umsdos + +Mostly... + +You mount a DOS partition like this + +mount -t umsdos /dev/hda3 /mnt + ^ +---------| + +All option are passed to the msdos drivers. Option like uid,gid etc are +given to msdos. + +The default behavior of Umsdos is to do the same thing as the msdos driver +mostly passing commands to it without much processing. Again, this is +the default. After doing the mount on a DOS partition, nothing special +happen. This is why all mount options are passed to the Msdos fs driver. + +Umsdos use a special DOS file --linux-.--- to store the information +which can't be handle by the normal MsDOS file system. This is the trick. + +--linux-.--- is optionnal. There is one per directory. + +**** If --linux-.--- is missing, then Umsdos process the directory the + same way the msdos driver do. Short file name, no goodies, default + owner and permissions. So each directory may have or not this + --linux-.--- + +Now, how to get those --linux-.---. + +\begin joke_section + + Well send me a directory content + and I will send you one customised for you. + $5 per directory. Add any applicable taxes. +\end joke_section + +A utility umssync creates those and maintain them. It is available +from the same directory above (sunsite) in the file umsdos_progs-0.3.tar.gz. +A compiled version is available in umsdos-0.3a.bin.tar.gz. + +So in our example, after mounting mnt, we do + +umssync . + +This will promote this directory (a recursive option is available) to full +umsdos capabilities (long name ...). A ls -l before and after won't show +much difference however. The file which were there are still there. But now +you can do all this: + + chmod 644 * + chown you.your_groupe * + ls >THIS_IS.A.VERY.LONG.NAME + ln -s toto tata + ls -l + +Once a directory is promoted, all subdirectory created will inherit that +promotion. + +What happen if you boot DOS and create files in those promoted directories ? +Umsdos won't notice new files, but will signal removed file (it won't crash). +Using umssync in /etc/rc will make sure the DOS directory is in sync with +the --linux-.---. + +Hope this helps! + diff -u --recursive --new-file v1.1.34/linux/fs/umsdos/check.c linux/fs/umsdos/check.c --- v1.1.34/linux/fs/umsdos/check.c Thu Jan 1 02:00:00 1970 +++ linux/fs/umsdos/check.c Sun Jul 24 17:39:44 1994 @@ -0,0 +1,55 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern unsigned long high_memory; + +static int check_one_table(unsigned long * page_dir) +{ + unsigned long pg_table = *page_dir; + + if (!pg_table) + return 0; + if (pg_table >= high_memory || !(pg_table & PAGE_PRESENT)) { + return 1; + } + return 0; +} + +/* + * This function frees up all page tables of a process when it exits. + */ +void check_page_tables(void) +{ + unsigned long pg_dir; + static int err = 0; + + int stack_level = (long)(&pg_dir)-current->kernel_stack_page; + if (stack_level < 1500) printk ("** %d ** ",stack_level); + pg_dir = current->tss.cr3; + if (mem_map[MAP_NR(pg_dir)] > 1) { + return; + } + if (err == 0){ + unsigned long *page_dir = (unsigned long *) pg_dir; + unsigned long *base = page_dir; + int i; + for (i = 0 ; i < PTRS_PER_PAGE ; i++,page_dir++){ + int notok = check_one_table(page_dir); + if (notok){ + err++; + printk ("|%d| ",page_dir-base); + } + } + if (err) printk ("Erreur MM %d\n",err); + } +} + diff -u --recursive --new-file v1.1.34/linux/fs/umsdos/dir.c linux/fs/umsdos/dir.c --- v1.1.34/linux/fs/umsdos/dir.c Thu Jan 1 02:00:00 1970 +++ linux/fs/umsdos/dir.c Sun Jul 24 17:39:44 1994 @@ -0,0 +1,703 @@ +/* + * linux/fs/umsdos/dir.c + * + * Written 1993 by Jacques Gelinas + * Inspired from linux/fs/msdos/... : Werner Almesberger + * + * Extended MS-DOS directory handling functions + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PRINTK(x) +#define Printk(x) printk x + +#define UMSDOS_SPECIAL_DIRFPOS 3 +extern struct inode *pseudo_root; +/* + So grep * doesn't complain in the presence of directories. +*/ +int UMSDOS_dir_read(struct inode *inode,struct file *filp,char *buf, + int count) +{ + return -EISDIR; +} +/* + Read count directory entries from directory filp + Return a negative value from linux/errno.h. + Return > 0 if success (the length of the file name). + + This function is used by the normal readdir VFS entry point and by + some function who try to find out info on a file from a pure MSDOS + inode. See umsdos_locate_ancestor() below. +*/ +static int umsdos_readdir_x( + struct inode *dir, /* Point to a description of the super block */ + struct file *filp, /* Point to a directory which is read */ + struct dirent *dirent, /* Will hold count directory entry */ + int dirent_in_fs, /* dirent point in user's space ? */ + int count, + struct umsdos_dirent *u_entry, /* Optionnal umsdos entry */ + int follow_hlink, + off_t *pt_f_pos) /* will hold the offset of the entry in EMD */ +{ + int ret = 0; + + umsdos_startlookup(dir); + if (filp->f_pos == UMSDOS_SPECIAL_DIRFPOS + && dir == pseudo_root + && dirent_in_fs){ + /* + We don't need to simulate this pseudo directory + when umsdos_readdir_x is called for internal operation + of umsdos. This is why dirent_in_fs is tested + */ + /* #Specification: pseudo root / directory /DOS + When umsdos operates in pseudo root mode (C:\linux is the + linux root), it simulate a directory /DOS which points to + the real root of the file system. + */ + put_fs_long(dir->i_sb->s_mounted->i_ino,&dirent->d_ino); + memcpy_tofs (dirent->d_name,"DOS",3); + put_fs_byte(0,dirent->d_name+3); + put_fs_word (3,&dirent->d_reclen); + if (u_entry != NULL) u_entry->flags = 0; + ret = 3; + filp->f_pos++; + }else if (filp->f_pos < 2 + || (dir != dir->i_sb->s_mounted && filp->f_pos == 32)){ + /* #Specification: readdir / . and .. + The msdos filesystem manage the . and .. entry properly + so the EMD file won't hold any info about it. + + In readdir, we assume that for the root directory + the read position will be 0 for ".", 1 for "..". For + a non root directory, the read position will be 0 for "." + and 32 for "..". + */ + /* + This is a trick used by the msdos file system (fs/msdos/dir.c) + to manage . and .. for the root directory of a file system. + Since there is no such entry in the root, fs/msdos/dir.c + use the following: + + if f_pos == 0, return ".". + if f_pos == 1, return "..". + + So let msdos handle it + + Since umsdos entries are much larger, we share the same f_pos. + if f_pos is 0 or 1 or 32, we are clearly looking at . and + .. + + As soon as we get f_pos == 2 or f_pos == 64, then back to + 0, but this time we are reading the EMD file. + + Well, not so true. The problem, is that UMSDOS_REC_SIZE is + also 64, so as soon as we read the first record in the + EMD, we are back at offset 64. So we set the offset + to UMSDOS_SPECIAL_DIRFPOS(3) as soon as we have read the + .. entry from msdos. + */ + ret = msdos_readdir(dir,filp,dirent,count); + if (filp->f_pos == 64) filp->f_pos = UMSDOS_SPECIAL_DIRFPOS; + if (u_entry != NULL) u_entry->flags = 0; + }else{ + struct inode *emd_dir = umsdos_emd_dir_lookup(dir,0); + if (emd_dir != NULL){ + if (filp->f_pos <= UMSDOS_SPECIAL_DIRFPOS+1) filp->f_pos = 0; + PRINTK (("f_pos %ld i_size %d\n",filp->f_pos,emd_dir->i_size)); + ret = 0; + while (filp->f_pos < emd_dir->i_size){ + struct umsdos_dirent entry; + off_t cur_f_pos = filp->f_pos; + if (umsdos_emd_dir_readentry (emd_dir,filp,&entry)!=0){ + ret = -EIO; + break; + }else if (entry.name_len != 0){ + /* #Specification: umsdos / readdir + umsdos_readdir() should fill a struct dirent with + an inode number. The cheap way to get it is to + do a lookup in the MSDOS directory for each + entry processed by the readdir() function. + This is not very efficient, but very simple. The + other way around is to maintain a copy of the inode + number in the EMD file. This is a problem because + this has to be maintained in sync using tricks. + Remember that MSDOS (the OS) does not update the + modification time (mtime) of a directory. There is + no easy way to tell that a directory was modified + during a DOS session and synchronise the EMD file. + + Suggestion welcome. + + So the easy way is used! + */ + struct umsdos_info info; + struct inode *inode; + int lret; + umsdos_parse (entry.name,entry.name_len,&info); + info.f_pos = cur_f_pos; + *pt_f_pos = cur_f_pos; + umsdos_manglename (&info); + lret = umsdos_real_lookup (dir,info.fake.fname + ,info.fake.len,&inode); + PRINTK (("Cherche inode de %s lret %d flags %d\n" + ,info.fake.fname,lret,entry.flags)); + if (lret == 0 + && (entry.flags & UMSDOS_HLINK) + && follow_hlink){ + struct inode *rinode; + lret = umsdos_hlink2inode (inode,&rinode); + inode = rinode; + } + if (lret == 0){ + /* #Specification: pseudo root / reading real root + The pseudo root (/linux) is logically + erased from the real root. This mean that + ls /DOS, won't show "linux". This avoids + infinite recursion /DOS/linux/DOS/linux while + walking the file system. + */ + if (inode != pseudo_root){ + PRINTK (("Trouve ino %d ",inode->i_ino)); + if (dirent_in_fs){ + put_fs_long(inode->i_ino,&dirent->d_ino); + memcpy_tofs (dirent->d_name,entry.name + ,entry.name_len); + put_fs_byte(0,dirent->d_name+entry.name_len); + put_fs_word (entry.name_len + ,&dirent->d_reclen); + /* In this case, the caller only needs */ + /* flags */ + if (u_entry != NULL){ + u_entry->flags = entry.flags; + } + }else{ + dirent->d_ino = inode->i_ino; + memcpy (dirent->d_name,entry.name + ,entry.name_len); + dirent->d_name[entry.name_len] = '\0'; + dirent->d_reclen = entry.name_len; + if (u_entry != NULL) *u_entry = entry; + } + ret = entry.name_len; + iput (inode); + break; + } + iput (inode); + }else{ + /* #Specification: umsdos / readdir / not in MSDOS + During a readdir operation, if the file is not + in the MSDOS directory anymore, the entry is + removed from the EMD file silently. + */ + ret = umsdos_writeentry (dir,emd_dir,&info,1); + if (ret != 0){ + break; + } + } + } + } + iput(emd_dir); + } + } + umsdos_endlookup(dir); + PRINTK (("read dir %p pos %d ret %d\n",dir,filp->f_pos,ret)); + return ret; +} +/* + Read count directory entries from directory filp + Return a negative value from linux/errno.h. + Return > 0 if success (the length of the file name). +*/ +static int UMSDOS_readdir( + struct inode *dir, /* Point to a description of the super block */ + struct file *filp, /* Point to a directory which is read */ + struct dirent *dirent, /* Will hold count directory entry */ + int count) +{ + int ret = -ENOENT; + while (1){ + struct umsdos_dirent entry; + off_t f_pos; + ret = umsdos_readdir_x (dir,filp,dirent,1,count,&entry,1,&f_pos); + if (ret <= 0 || !(entry.flags & UMSDOS_HIDDEN)) break; + } + return ret; +} +/* + Complete the inode content with info from the EMD file +*/ +void umsdos_lookup_patch ( + struct inode *dir, + struct inode *inode, + struct umsdos_dirent *entry, + off_t emd_pos) +{ + /* + This function modify the state of a dir inode. It decides + if the dir is a umsdos dir or a dos dir. This is done + deeper in umsdos_patch_inode() called at the end of this function. + + umsdos_patch_inode() may block because it is doing disk access. + At the same time, another process may get here to initialise + the same dir inode. There is 3 cases. + + 1-The inode is already initialised. We do nothing. + 2-The inode is not initialised. We lock access and do it. + 3-Like 2 but another process has lock the inode, so we try + to lock it and right after check if initialisation is still + needed. + + + Thanks to the mem option of the kernel command line, it was + possible to consistently reproduce this problem by limiting + my mem to 4 meg and running X. + */ + /* + Do this only if the inode is freshly read, because we will lose + the current (updated) content. + */ + /* + A lookup of a mount point directory yield the inode into + the other fs, so we don't care about initialising it. iget() + does this automaticly. + */ + if (inode->i_sb == dir->i_sb && !umsdos_isinit(inode)){ + if (S_ISDIR(inode->i_mode)) umsdos_lockcreate(inode); + if (!umsdos_isinit(inode)){ + /* #Specification: umsdos / lookup / inode info + After successfully reading an inode from the MSDOS + filesystem, we use the EMD file to complete it. + We update the following field. + + uid, gid, atime, ctime, mtime, mode. + + We rely on MSDOS for mtime. If the file + was modified during an MSDOS session, at least + mtime will be meaningful. We do this only for regular + file. + + We don't rely on MSDOS for mtime for directory because + the MSDOS directory date is creation time (strange + MSDOS behavior) which fit nowhere in the three UNIX + time stamp. + */ + if (S_ISREG(entry->mode)) entry->mtime = inode->i_mtime; + inode->i_mode = entry->mode; + inode->i_rdev = entry->rdev; + inode->i_atime = entry->atime; + inode->i_ctime = entry->ctime; + inode->i_mtime = entry->mtime; + inode->i_uid = entry->uid; + inode->i_gid = entry->gid; + /* #Specification: umsdos / i_nlink + The nlink field of an inode is maintain by the MSDOS file system + for directory and by UMSDOS for other file. The logic is that + MSDOS is already figuring out what to do for directories and + does nothing for other files. For MSDOS, there are no hard link + so all file carry nlink==1. UMSDOS use some info in the + EMD file to plug the correct value. + */ + if (!S_ISDIR(entry->mode)){ + if (entry->nlink > 0){ + inode->i_nlink = entry->nlink; + }else{ + printk ("UMSDOS: lookup_patch entry->nlink < 1 ???\n"); + } + } + umsdos_patch_inode(inode,dir,emd_pos); + } + if (S_ISDIR(inode->i_mode)) umsdos_unlockcreate(inode); +if (inode->u.umsdos_i.i_emd_owner==0) printk ("emd_owner still 0 ???\n"); + } +} +/* + Locate entry of an inode in a directory. + Return 0 or a negative error code. + + Normally, this function must succeed. It means a strange corruption + in the file system if not. +*/ +int umsdos_inode2entry ( + struct inode *dir, + struct inode *inode, + struct umsdos_dirent *entry) /* Will hold the entry */ +{ + int ret = -ENOENT; + if (inode == pseudo_root){ + /* + Quick way to find the name. + Also umsdos_readdir_x won't show /linux anyway + */ + memcpy (entry->name,UMSDOS_PSDROOT_NAME,UMSDOS_PSDROOT_LEN+1); + entry->name_len = UMSDOS_PSDROOT_LEN; + ret = 0; + }else{ + struct inode *emddir = umsdos_emd_dir_lookup(dir,0); + iput (emddir); + if (emddir == NULL){ + /* This is a DOS directory */ + struct file filp; + filp.f_pos = 0; + while (1){ + struct dirent dirent; + if (umsdos_readdir_kmem (dir,&filp,&dirent,1) <= 0){ + printk ("UMSDOS: can't locate inode %ld in DOS directory???\n" + ,inode->i_ino); + }else if (dirent.d_ino == inode->i_ino){ + ret = 0; + memcpy (entry->name,dirent.d_name,dirent.d_reclen); + entry->name[dirent.d_reclen] = '\0'; + entry->name_len = dirent.d_reclen; + inode->u.umsdos_i.i_dir_owner = dir->i_ino; + inode->u.umsdos_i.i_emd_owner = 0; + umsdos_setup_dir_inode(inode); + break; + } + } + }else{ + /* skip . and .. see umsdos_readdir_x() */ + struct file filp; + filp.f_pos = UMSDOS_SPECIAL_DIRFPOS; + while (1){ + struct dirent dirent; + off_t f_pos; + if (umsdos_readdir_x(dir,&filp,&dirent + ,0,1,entry,0,&f_pos) <= 0){ + printk ("UMSDOS: can't locate inode %ld in EMD file???\n" + ,inode->i_ino); + break; + }else if (dirent.d_ino == inode->i_ino){ + ret = 0; + umsdos_lookup_patch (dir,inode,entry,f_pos); + break; + } + } + } + } + return ret; +} +/* + Locate the parent of a directory and the info on that directory + Return 0 or a negative error code. +*/ +static int umsdos_locate_ancestor ( + struct inode *dir, + struct inode **result, + struct umsdos_dirent *entry) +{ + int ret; + umsdos_patch_inode (dir,NULL,0); + ret = umsdos_real_lookup (dir,"..",2,result); + PRINTK (("result %d %x ",ret,*result)); + if (ret == 0){ + struct inode *adir = *result; + ret = umsdos_inode2entry (adir,dir,entry); + } + PRINTK (("\n")); + return ret; +} +/* + Build the path name of an inode (relative to the file system. + This function is need to set (pseudo) hard link. + + It uses the same strategy as the standard getcwd(). +*/ +int umsdos_locate_path ( + struct inode *inode, + char *path) +{ + int ret = 0; + struct inode *dir = inode; + char *bpath = (char*)kmalloc(PATH_MAX,GFP_KERNEL); + if (bpath == NULL){ + ret = -ENOMEM; + }else{ + struct umsdos_dirent entry; + char *ptbpath = bpath+PATH_MAX-1; + *ptbpath = '\0'; + PRINTK (("locate_path mode %x ",inode->i_mode)); + if (!S_ISDIR(inode->i_mode)){ + ret = umsdos_get_dirowner (inode,&dir); + PRINTK (("locate_path ret %d ",ret)); + if (ret == 0){ + ret = umsdos_inode2entry (dir,inode,&entry); + if (ret == 0){ + ptbpath -= entry.name_len; + memcpy (ptbpath,entry.name,entry.name_len); + PRINTK (("ptbpath :%s: ",ptbpath)); + } + } + }else{ + dir->i_count++; + } + if (ret == 0){ + while (dir != dir->i_sb->s_mounted){ + struct inode *adir; + ret = umsdos_locate_ancestor (dir,&adir,&entry); + iput (dir); + dir = NULL; + PRINTK (("ancestor %d ",ret)); + if (ret == 0){ + *--ptbpath = '/'; + ptbpath -= entry.name_len; + memcpy (ptbpath,entry.name,entry.name_len); + dir = adir; + PRINTK (("ptbpath :%s: ",ptbpath)); + }else{ + break; + } + } + } + strcpy (path,ptbpath); + kfree (bpath); + } + PRINTK (("\n")); + iput (dir); + return ret; +} + +/* + Return != 0 if an entry is the pseudo DOS entry in the pseudo root. +*/ +int umsdos_is_pseudodos ( + struct inode *dir, + const char *name, + int len) +{ + /* #Specification: pseudo root / DOS hard coded + The pseudo sub-directory DOS in the pseudo root is hard coded. + The name is DOS. This is done this way to help standardised + the umsdos layout. The idea is that from now on /DOS is + a reserved path and nobody will think of using such a path + for a package. + */ + return dir == pseudo_root + && len == 3 + && name[0] == 'D' && name[1] == 'O' && name[2] == 'S'; +} +/* + Check if a file exist in the current directory. + Return 0 if ok, negative error code if not (ex: -ENOENT). +*/ +static int umsdos_lookup_x ( + struct inode *dir, + const char *name, + int len, + struct inode **result, /* Will hold inode of the file, if successful */ + int nopseudo) /* Don't care about pseudo root mode */ +{ + int ret = -ENOENT; + *result = NULL; + umsdos_startlookup(dir); + if (len == 1 && name[0] == '.'){ + *result = dir; + dir->i_count++; + ret = 0; + }else if (len == 2 && name[0] == '.' && name[1] == '.'){ + if (pseudo_root != NULL && dir == pseudo_root->i_sb->s_mounted){ + /* #Specification: pseudo root / .. in real root + Whenever a lookup is those in the real root for + the directory .., and pseudo root is active, the + pseudo root is returned. + */ + ret = 0; + *result = pseudo_root; + pseudo_root->i_count++; + }else{ + /* #Specification: locating .. / strategy + We use the msdos filesystem to locate the parent directory. + But it is more complicated than that. + + We have to step back even further to + get the parent of the parent, so we can get the EMD + of the parent of the parent. Using the EMD file, we can + locate all the info on the parent, such a permissions + and owner. + */ + ret = umsdos_real_lookup (dir,"..",2,result); + PRINTK (("ancestor ret %d dir %p *result %p ",ret,dir,*result)); + if (ret == 0 + && *result != dir->i_sb->s_mounted + && *result != pseudo_root){ + struct inode *aadir; + struct umsdos_dirent entry; + ret = umsdos_locate_ancestor (*result,&aadir,&entry); + iput (aadir); + } + } + }else if (umsdos_is_pseudodos(dir,name,len)){ + /* #Specification: pseudo root / lookup(DOS) + A lookup of DOS in the pseudo root will always succeed + and return the inode of the real root. + */ + *result = dir->i_sb->s_mounted; + (*result)->i_count++; + ret = 0; + }else{ + struct umsdos_info info; + ret = umsdos_parse (name,len,&info); + if (ret == 0) ret = umsdos_findentry (dir,&info,0); + PRINTK (("lookup %s pos %d ret %d len %d ",info.fake.fname,info.f_pos,ret + ,info.fake.len)); + if (ret == 0){ + /* #Specification: umsdos / lookup + A lookup for a file is done in two step. First, we locate + the file in the EMD file. If not present, we return + an error code (-ENOENT). If it is there, we repeat the + operation on the msdos file system. If this fails, it means + that the file system is not in sync with the emd file. + We silently remove this entry from the emd file, + and return ENOENT. + */ + struct inode *inode; + ret = umsdos_real_lookup (dir,info.fake.fname,info.fake.len,result); + inode = *result; + if (inode == NULL){ + printk ("UMSDOS: Erase entry %s, out of sync with MsDOS\n" + ,info.fake.fname); + umsdos_delentry (dir,&info,S_ISDIR(info.entry.mode)); + }else{ + umsdos_lookup_patch (dir,inode,&info.entry,info.f_pos); + PRINTK (("lookup ino %d flags %d\n",inode->i_ino + ,info.entry.flags)); + if (info.entry.flags & UMSDOS_HLINK){ + ret = umsdos_hlink2inode (inode,result); + } + if (*result == pseudo_root && !nopseudo){ + /* #Specification: pseudo root / dir lookup + For the same reason as readdir, a lookup in /DOS for + the pseudo root directory (linux) will fail. + */ + /* + This has to be allowed for resolving hard link + which are recorded independantly of the pseudo-root + mode. + */ + iput (pseudo_root); + *result = NULL; + ret = -ENOENT; + } + } + } + } + umsdos_endlookup(dir); + iput (dir); + return ret; +} +/* + Check if a file exist in the current directory. + Return 0 if ok, negative error code if not (ex: -ENOENT). +*/ +int UMSDOS_lookup ( + struct inode *dir, + const char *name, + int len, + struct inode **result) /* Will hold inode of the file, if successful */ +{ + return umsdos_lookup_x(dir,name,len,result,0); +} +/* + Locate the inode pointed by a (pseudo) hard link + Return 0 if ok, a negative error code if not. +*/ +int umsdos_hlink2inode (struct inode *hlink, struct inode **result) +{ + int ret = -EIO; + char *path = (char*)kmalloc(PATH_MAX,GFP_KERNEL); + *result = NULL; + if (path == NULL){ + ret = -ENOMEM; + iput (hlink); + }else{ + struct file filp; + filp.f_pos = 0; + PRINTK (("hlink2inode ")); + if (umsdos_file_read_kmem (hlink,&filp,path,hlink->i_size) + ==hlink->i_size){ + struct inode *dir; + char *pt = path; + dir = hlink->i_sb->s_mounted; + path[hlink->i_size] = '\0'; + iput (hlink); + dir->i_count++; + while (1){ + char *start = pt; + int len; + while (*pt != '\0' && *pt != '/') pt++; + len = (int)(pt - start); + if (*pt == '/') *pt++ = '\0'; + if (dir->u.umsdos_i.i_emd_dir == 0){ + /* This is a DOS directory */ + ret = msdos_lookup(dir,start,len,result); + }else{ + ret = umsdos_lookup_x(dir,start,len,result,1); + } + PRINTK (("h2n lookup :%s: -> %d ",start,ret)); + if (ret == 0 && *pt != '\0'){ + dir = *result; + }else{ + break; + } + } + }else{ + iput (hlink); + } + PRINTK (("hlink2inode ret = %d %p -> %p\n",ret,hlink,*result)); + kfree (path); + } + return ret; +} + +static struct file_operations umsdos_dir_operations = { + NULL, /* lseek - default */ + UMSDOS_dir_read, /* read */ + NULL, /* write - bad */ + UMSDOS_readdir, /* readdir */ + NULL, /* select - default */ + UMSDOS_ioctl_dir, /* ioctl - default */ + NULL, /* mmap */ + NULL, /* no special open code */ + NULL, /* no special release code */ + NULL /* fsync */ +}; + +struct inode_operations umsdos_dir_inode_operations = { + &umsdos_dir_operations, /* default directory file-ops */ + UMSDOS_create, /* create */ + UMSDOS_lookup, /* lookup */ + UMSDOS_link, /* link */ + UMSDOS_unlink, /* unlink */ + UMSDOS_symlink, /* symlink */ + UMSDOS_mkdir, /* mkdir */ + UMSDOS_rmdir, /* rmdir */ + UMSDOS_mknod, /* mknod */ + UMSDOS_rename, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL /* permission */ +}; + + + + + + + + + + diff -u --recursive --new-file v1.1.34/linux/fs/umsdos/emd.c linux/fs/umsdos/emd.c --- v1.1.34/linux/fs/umsdos/emd.c Thu Jan 1 02:00:00 1970 +++ linux/fs/umsdos/emd.c Sun Jul 24 17:39:45 1994 @@ -0,0 +1,499 @@ +/* + * linux/fs/umsdos/emd.c + * + * Written 1993 by Jacques Gelinas + * + * Extended MS-DOS directory handling functions + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PRINTK(x) +#define Printk(x) printk x + +int umsdos_readdir_kmem( + struct inode *inode, + struct file *filp, + struct dirent *dirent, + int count) +{ + int ret; + set_fs (KERNEL_DS); + ret = msdos_readdir(inode,filp,dirent,count); + set_fs (USER_DS); + return ret; +} +/* + Read a file into kernel space memory +*/ +int umsdos_file_read_kmem( + struct inode *inode, + struct file *filp, + char *buf, + int count) +{ + int ret; + set_fs (KERNEL_DS); + ret = msdos_file_read(inode,filp,buf,count); + set_fs (USER_DS); + return ret; +} +/* + Write to a file from kernel space +*/ +int umsdos_file_write_kmem( + struct inode *inode, + struct file *filp, + char *buf, + int count) +{ + int ret; + set_fs (KERNEL_DS); + ret = msdos_file_write(inode,filp,buf,count); + set_fs (USER_DS); + return ret; +} + + +/* + Write a block of bytes into one EMD file. + The block of data is NOT in user space. + + Return 0 if ok, a negative error code if not. +*/ +int umsdos_emd_dir_write ( + struct inode *emd_dir, + struct file *filp, + char *buf, /* buffer in kernel memory, not in user space */ + int count) +{ + int written; + filp->f_flags = 0; + written = umsdos_file_write_kmem (emd_dir,filp,buf,count); + return written != count ? -EIO : 0; +} +/* + Read a block of bytes from one EMD file. + The block of data is NOT in user space. + Retourne 0 if ok, -EIO if any error. +*/ +int umsdos_emd_dir_read ( + struct inode *emd_dir, + struct file *filp, + char *buf, /* buffer in kernel memory, not in user space */ + int count) +{ + int ret = 0; + int sizeread; + filp->f_flags = 0; + sizeread = umsdos_file_read_kmem (emd_dir,filp,buf,count); + if (sizeread != count){ + printk ("UMSDOS: problem with EMD file. Can't read\n"); + ret = -EIO; + } + return ret; + +} +/* + Locate the EMD file in a directory and optionnally, creates it. + + Return NULL if error. If ok, dir->u.umsdos_i.emd_inode +*/ +struct inode *umsdos_emd_dir_lookup(struct inode *dir, int creat) +{ + struct inode *ret = NULL; + if (dir->u.umsdos_i.i_emd_dir != 0){ + ret = iget (dir->i_sb,dir->u.umsdos_i.i_emd_dir); + PRINTK (("deja trouve %d %x [%d] " + ,dir->u.umsdos_i.i_emd_dir,ret,ret->i_count)); + }else{ + umsdos_real_lookup (dir,UMSDOS_EMD_FILE,UMSDOS_EMD_NAMELEN,&ret); + PRINTK (("emd_dir_lookup ")); + if (ret != NULL){ + PRINTK (("Find --linux ")); + dir->u.umsdos_i.i_emd_dir = ret->i_ino; + }else if (creat){ + int code; + PRINTK (("avant create ")); + dir->i_count++; + code = msdos_create (dir,UMSDOS_EMD_FILE,UMSDOS_EMD_NAMELEN + ,S_IFREG|0777,&ret); + PRINTK (("Creat EMD code %d ret %x ",code,ret)); + if (ret != NULL){ + dir->u.umsdos_i.i_emd_dir = ret->i_ino; + }else{ + printk ("UMSDOS: Can't create EMD file\n"); + } + } + } + if (ret != NULL){ + /* Disable UMSDOS_notify_change() for EMD file */ + ret->u.umsdos_i.i_emd_owner = 0xffffffff; + } + return ret; +} + +/* + Read an entry from the EMD file. + Support variable length record. + Return -EIO if error, 0 if ok. +*/ +int umsdos_emd_dir_readentry ( + struct inode *emd_dir, + struct file *filp, + struct umsdos_dirent *entry) +{ + int ret = umsdos_emd_dir_read(emd_dir,filp,(char*)entry,UMSDOS_REC_SIZE); + if (ret == 0){ + /* Variable size record. Maybe, we have to read some more */ + int recsize = umsdos_evalrecsize (entry->name_len); + if (recsize > UMSDOS_REC_SIZE){ + ret = umsdos_emd_dir_read(emd_dir,filp + ,((char*)entry)+UMSDOS_REC_SIZE,recsize - UMSDOS_REC_SIZE); + + } + } + return ret; +} +/* + Write an entry in the EMD file. + Return 0 if ok, -EIO if some error. +*/ +int umsdos_writeentry ( + struct inode *dir, + struct inode *emd_dir, + struct umsdos_info *info, + int free_entry) /* This entry is deleted, so Write all 0's */ +{ + int ret = 0; + struct file filp; + struct umsdos_dirent *entry = &info->entry; + struct umsdos_dirent entry0; + if (free_entry){ + /* #Specification: EMD file / empty entries + Unused entry in the EMD file are identify + by the name_len field equal to 0. However to + help future extension (or bug corretion :-( ), + empty entries are filled with 0. + */ + memset (&entry0,0,sizeof(entry0)); + entry = &entry0; + }else if (entry->name_len > 0){ + memset (entry->name+entry->name_len,'\0' + ,sizeof(entry->name)-entry->name_len); + /* #Specification: EMD file / spare bytes + 10 bytes are unused in each record of the EMD. They + are set to 0 all the time. So it will be possible + to do new stuff and rely on the state of those + bytes in old EMD file around. + */ + memset (entry->spare,0,sizeof(entry->spare)); + } + filp.f_pos = info->f_pos; + ret = umsdos_emd_dir_write(emd_dir,&filp,(char*)entry,info->recsize); + if (ret != 0){ + printk ("UMSDOS: problem with EMD file. Can't write\n"); + }else{ + dir->i_ctime = dir->i_mtime = CURRENT_TIME; + dir->i_dirt = 1; + } + return ret; +} + +#define CHUNK_SIZE (16*UMSDOS_REC_SIZE) +struct find_buffer{ + char buffer[CHUNK_SIZE]; + int pos; /* read offset in buffer */ + int size; /* Current size of buffer */ + struct file filp; +}; + +/* + Fill the read buffer and take care of the byte remaining inside. + Unread bytes are simply move to the beginning. + + Return -ENOENT if EOF, 0 if ok, a negativ error code if any problem. +*/ +static int umsdos_fillbuf ( + struct inode *inode, + struct find_buffer *buf) +{ + int ret = -ENOENT; + int mustmove = buf->size - buf->pos; + int mustread; + int remain; + if (mustmove > 0){ + memcpy (buf->buffer,buf->buffer+buf->pos,mustmove); + } + buf->pos = 0; + mustread = CHUNK_SIZE - mustmove; + remain = inode->i_size - buf->filp.f_pos; + if (remain < mustread) mustread = remain; + if (mustread > 0){ + ret = umsdos_emd_dir_read (inode,&buf->filp,buf->buffer+mustmove + ,mustread); + if (ret == 0) buf->size = mustmove + mustread; + }else if (mustmove){ + buf->size = mustmove; + ret = 0; + } + return ret; +} + +/* + General search, locate a name in the EMD file or an empty slot to + store it. if info->entry.name_len == 0, search the first empty + slot (of the proper size). + + Caller must do iput on *pt_emd_dir. + + Return 0 if found, -ENOENT if not found, another error code if + other problem. + + So this routine is used to either find an existing entry or to + create a new one, while making sure it is a new one. After you + get -ENOENT, you make sure the entry is stuffed correctly and + call umsdos_writeentry(). + + To delete an entry, you find it, zero out the entry (memset) + and call umsdos_writeentry(). + + All this to say that umsdos_writeentry must be call after this + function since it rely on the f_pos field of info. +*/ +static int umsdos_find ( + struct inode *dir, + struct umsdos_info *info, /* Hold name and name_len */ + /* Will hold the entry found */ + struct inode **pt_emd_dir) /* Will hold the emd_dir inode */ + /* or NULL if not found */ +{ + /* #Specification: EMD file structure + The EMD file uses a fairly simple layout. It is made of records + (UMSDOS_REC_SIZE == 64). When a name can't be written is a single + record, multiple contiguous record are allocated. + */ + int ret = -ENOENT; + struct inode *emd_dir = umsdos_emd_dir_lookup(dir,1); + if (emd_dir != NULL){ + struct umsdos_dirent *entry = &info->entry; + int recsize = info->recsize; + struct { + off_t posok; /* Position available to store the entry */ + int found; /* A valid empty position has been found */ + off_t one; /* One empty position -> maybe <- large enough */ + int onesize; /* size of empty region starting at one */ + }empty; + /* Read several entries at a time to speed up the search */ + struct find_buffer buf; + buf.pos = 0; + buf.size = 0; + buf.filp.f_pos = 0; + empty.found = 0; + empty.posok = emd_dir->i_size; + empty.onesize = 0; + while (1){ + struct umsdos_dirent *rentry = (struct umsdos_dirent*) + (buf.buffer + buf.pos); + int file_pos = buf.filp.f_pos - buf.size + buf.pos; + if (buf.pos == buf.size){ + ret = umsdos_fillbuf (emd_dir,&buf); + if (ret < 0){ + /* Not found, so note where it can be added */ + info->f_pos = empty.posok; + break; + } + }else if (rentry->name_len == 0){ + /* We are looking for an empty section at least */ + /* recsize large */ + if (entry->name_len == 0){ + info->f_pos = file_pos; + ret = 0; + break; + }else if (!empty.found){ + if (empty.onesize == 0){ + /* This is the first empty record of a section */ + empty.one = file_pos; + } + /* grow the empty section */ + empty.onesize += UMSDOS_REC_SIZE; + if (empty.onesize == recsize){ + /* here is a large enough section */ + empty.posok = empty.one; + empty.found = 1; + } + } + buf.pos += UMSDOS_REC_SIZE; + }else{ + int entry_size = umsdos_evalrecsize(rentry->name_len); + if (buf.pos+entry_size > buf.size){ + ret = umsdos_fillbuf (emd_dir,&buf); + if (ret < 0){ + /* Not found, so note where it can be added */ + info->f_pos = empty.posok; + break; + } + }else{ + empty.onesize = 0; /* Reset the free slot search */ + if (entry->name_len == rentry->name_len + && memcmp(entry->name,rentry->name,rentry->name_len) + ==0){ + info->f_pos = file_pos; + *entry = *rentry; + ret = 0; + break; + }else{ + buf.pos += entry_size; + } + } + } + } + umsdos_manglename(info); + } + *pt_emd_dir = emd_dir; + return ret; +} +/* + Add a new entry in the emd file + Return 0 if ok or a negative error code. + Return -EEXIST if the entry already exist. + + Complete the information missing in info. +*/ +int umsdos_newentry ( + struct inode *dir, + struct umsdos_info *info) +{ + struct inode *emd_dir; + int ret = umsdos_find (dir,info,&emd_dir); + if (ret == 0){ + ret = -EEXIST; + }else if (ret == -ENOENT){ + ret = umsdos_writeentry(dir,emd_dir,info,0); + PRINTK (("umsdos_newentry EDM ret = %d\n",ret)); + } + iput (emd_dir); + return ret; +} +/* + Create a new hidden link. + Return 0 if ok, an error code if not. +*/ +int umsdos_newhidden ( + struct inode *dir, + struct umsdos_info *info) +{ + struct inode *emd_dir; + int ret; + umsdos_parse ("..LINK",6,info); + info->entry.name_len = 0; + ret = umsdos_find (dir,info,&emd_dir); + iput (emd_dir); + if (ret == -ENOENT || ret == 0){ + /* #Specification: hard link / hidden name + When a hard link is created, the original file is renamed + to a hidden name. The name is "..LINKNNN" where NNN is a + number define from the entry offset in the EMD file. + */ + info->entry.name_len = sprintf (info->entry.name,"..LINK%ld" + ,info->f_pos); + ret = 0; + } + return ret; +} +/* + Remove an entry from the emd file + Return 0 if ok, a negative error code otherwise. + + Complete the information missing in info. +*/ +int umsdos_delentry ( + struct inode *dir, + struct umsdos_info *info, + int isdir) +{ + struct inode *emd_dir; + int ret = umsdos_find (dir,info,&emd_dir); + if (ret == 0){ + if (info->entry.name_len != 0){ + if ((isdir != 0) != (S_ISDIR(info->entry.mode) != 0)){ + if (S_ISDIR(info->entry.mode)){ + ret = -EISDIR; + }else{ + ret = -ENOTDIR; + } + }else{ + ret = umsdos_writeentry(dir,emd_dir,info,1); + } + } + } + iput(emd_dir); + return ret; +} + + +/* + Verify is a EMD directory is empty. + Return 0 if not empty + 1 if empty + 2 if empty, no EMD file. +*/ +int umsdos_isempty (struct inode *dir) +{ + int ret = 2; + struct inode *emd_dir = umsdos_emd_dir_lookup(dir,0); + /* If the EMD file does not exist, it is certainly empty :-) */ + if (emd_dir != NULL){ + struct file filp; + /* Find an empty slot */ + filp.f_pos = 0; + filp.f_flags = O_RDONLY; + ret = 1; + while (filp.f_pos < emd_dir->i_size){ + struct umsdos_dirent entry; + if (umsdos_emd_dir_readentry(emd_dir,&filp,&entry)!=0){ + ret = 0; + break; + }else if (entry.name_len != 0){ + ret = 0; + break; + } + } + iput (emd_dir); + } + return ret; +} + +/* + Locate an entry in a EMD directory. + Return 0 if ok, errcod if not, generally -ENOENT. +*/ +int umsdos_findentry ( + struct inode *dir, + struct umsdos_info *info, + int expect) /* 0: anything */ + /* 1: file */ + /* 2: directory */ +{ + struct inode *emd_dir; + int ret = umsdos_find (dir,info,&emd_dir); + if (ret == 0){ + if (expect != 0){ + if (S_ISDIR(info->entry.mode)){ + if (expect != 2) ret = -EISDIR; + }else if (expect == 2){ + ret = -ENOTDIR; + } + } + } + iput (emd_dir); + return ret; +} + diff -u --recursive --new-file v1.1.34/linux/fs/umsdos/file.c linux/fs/umsdos/file.c --- v1.1.34/linux/fs/umsdos/file.c Thu Jan 1 02:00:00 1970 +++ linux/fs/umsdos/file.c Sun Jul 24 17:39:45 1994 @@ -0,0 +1,103 @@ +/* + * linux/fs/umsdos/file.c + * + * Written 1993 by Jacques Gelinas + * inpired from linux/fs/msdos/file.c Werner Almesberger + * + * Extended MS-DOS regular file handling primitives + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +#define PRINTK(x) +#define Printk(x) printk x +/* + Read a file into user space memory +*/ +static int UMSDOS_file_read( + struct inode *inode, + struct file *filp, + char *buf, + int count) +{ + /* We have to set the access time because msdos don't care */ + int ret = msdos_file_read(inode,filp,buf,count); + inode->i_atime = CURRENT_TIME; + inode->i_dirt = 1; + return ret; +} +/* + Write a file from user space memory +*/ +static int UMSDOS_file_write( + struct inode *inode, + struct file *filp, + char *buf, + int count) +{ + return msdos_file_write(inode,filp,buf,count); +} +/* + Truncate a file to 0 length. +*/ +static void UMSDOS_truncate(struct inode *inode) +{ + PRINTK (("UMSDOS_truncate\n")); + msdos_truncate (inode); + inode->i_ctime = inode->i_mtime = CURRENT_TIME; + inode->i_dirt = 1; +} +/* + See inode.c + + Some entry point are filled dynamicly with function pointers + from the msdos file_operations and file_inode_operations. + + The idea is to have the code as independant as possible from + the msdos file system. +*/ + +struct file_operations umsdos_file_operations = { + NULL, /* lseek - default */ + UMSDOS_file_read, /* read */ + UMSDOS_file_write, /* write */ + NULL, /* readdir - bad */ + NULL, /* select - default */ + NULL, /* ioctl - default */ + msdos_mmap, /* mmap */ + NULL, /* no special open is needed */ + NULL, /* release */ + NULL /* fsync */ +}; + +struct inode_operations umsdos_file_inode_operations = { + &umsdos_file_operations, /* default file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* bmap */ + UMSDOS_truncate,/* truncate */ + NULL, /* permission */ + msdos_smap /* smap */ +}; + + diff -u --recursive --new-file v1.1.34/linux/fs/umsdos/inode.c linux/fs/umsdos/inode.c --- v1.1.34/linux/fs/umsdos/inode.c Thu Jan 1 02:00:00 1970 +++ linux/fs/umsdos/inode.c Sun Jul 24 17:39:45 1994 @@ -0,0 +1,466 @@ +/* + * linux/fs/umsdos/inode.c + * + * Written 1993 by Jacques Gelinas + * Inspired from linux/fs/msdos/... by Werner Almesberger + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct inode *pseudo_root=NULL; /* Useful to simulate the pseudo DOS */ + /* directory. See UMSDOS_readdir_x() */ + +/* #Specification: convention / PRINTK Printk and printk + Here is the convention for the use of printk inside fs/umsdos + + printk carry important message (error or status). + Printk is for debugging (it is a macro defined at the beginning of + most source. + PRINTK is a nulled Printk macro. + + This convention makes the source easier to read, and Printk easier + to shut off. +*/ +#define PRINTK(x) +#define Printk(x) printk x + + +void UMSDOS_put_inode(struct inode *inode) +{ + PRINTK (("put inode %x owner %x pos %d dir %x\n",inode + ,inode->u.umsdos_i.i_emd_owner,inode->u.umsdos_i.pos + ,inode->u.umsdos_i.i_emd_dir)); + msdos_put_inode(inode); +} + + +void UMSDOS_put_super(struct super_block *sb) +{ + msdos_put_super(sb); +} + + +void UMSDOS_statfs(struct super_block *sb,struct statfs *buf) +{ + msdos_statfs(sb,buf); +} + + +/* + Call msdos_lookup, but set back the original msdos function table. + Retourne 0 if ok, or a negative error code if not. +*/ +int umsdos_real_lookup ( + struct inode *dir, + const char *name, + int len, + struct inode **result) /* Will hold inode of the file, if successful */ +{ + int ret; + dir->i_count++; + ret = msdos_lookup (dir,name,len,result); + return ret; +} +/* + Complete the setup of an directory inode. + First, it completes the function pointers, then + it locates the EMD file. If the EMD is there, then plug the + umsdos function table. If not, use the msdos one. +*/ +void umsdos_setup_dir_inode (struct inode *inode) +{ + inode->u.umsdos_i.i_emd_dir = 0; + { + struct inode *emd_dir = umsdos_emd_dir_lookup (inode,0); + extern struct inode_operations umsdos_rdir_inode_operations; + inode->i_op = emd_dir != NULL + ? &umsdos_dir_inode_operations + : &umsdos_rdir_inode_operations; + iput (emd_dir); + } +} +/* + Add some info into an inode so it can find its owner quickly +*/ +void umsdos_set_dirinfo( + struct inode *inode, + struct inode *dir, + off_t f_pos) +{ + struct inode *emd_owner = umsdos_emd_dir_lookup(dir,1); + inode->u.umsdos_i.i_dir_owner = dir->i_ino; + inode->u.umsdos_i.i_emd_owner = emd_owner->i_ino; + iput (emd_owner); + inode->u.umsdos_i.pos = f_pos; +} +/* + Tells if an Umsdos inode has been "patched" once. + Returne != 0 if so. +*/ +int umsdos_isinit (struct inode *inode) +{ +#if 1 + return inode->u.umsdos_i.i_emd_owner != 0; +#elif 0 + return inode->i_atime != 0; +#else + return inode->i_count > 1; +#endif +} +/* + Connect the proper tables in the inode and add some info. +*/ +void umsdos_patch_inode ( + struct inode *inode, + struct inode *dir, /* May be NULL */ + off_t f_pos) +{ + /* + This function is called very early to setup the inode, somewhat + too early (called by UMSDOS_read_inode). At this point, we can't + do to much, such as lookup up EMD files and so on. This causes + confusion in the kernel. This is why some initialisation + will be done when dir != NULL only. + + UMSDOS do run piggy back on top of msdos fs. It looks like something + is missing in the VFS to accomodate stacked fs. Still unclear what + (quite honestly). + + Well, maybe one! A new entry "may_unmount" which would allow + the stacked fs to allocate some inode permanently and release + them at the end. Doing that now introduce a problem. unmount + always fail because some inodes are in use. + */ + if (!umsdos_isinit(inode)){ + inode->u.umsdos_i.i_emd_dir = 0; + if (S_ISREG(inode->i_mode)){ + static char is_init = 0; + if (!is_init){ + /* + I don't want to change the msdos file system code + so I get the adress of some subroutine dynamicly + once. + */ + umsdos_file_inode_operations.bmap = inode->i_op->bmap; + inode->i_op = &umsdos_file_inode_operations; + is_init = 1; + } + inode->i_op = &umsdos_file_inode_operations; + }else if (S_ISDIR(inode->i_mode)){ + if (dir != NULL){ + umsdos_setup_dir_inode(inode); + } + }else if (S_ISLNK(inode->i_mode)){ + inode->i_op = &umsdos_symlink_inode_operations; + }else if (S_ISCHR(inode->i_mode)){ + inode->i_op = &chrdev_inode_operations; + }else if (S_ISBLK(inode->i_mode)){ + inode->i_op = &blkdev_inode_operations; + }else if (S_ISFIFO(inode->i_mode)){ + init_fifo(inode); + } + if (dir != NULL){ + /* #Specification: inode / umsdos info + The first time an inode is seen (inode->i_count == 1), + the inode number of the EMD file which control this inode + is tagged to this inode. It allows operation such + as notify_change to be handled. + */ + /* + This is done last because it also control the + status of umsdos_isinit() + */ + umsdos_set_dirinfo (inode,dir,f_pos); + } + }else if (dir != NULL){ + /* + Test to see if the info is maintained. + This should be removed when the file system will be proven. + */ + struct inode *emd_owner = umsdos_emd_dir_lookup(dir,1); + iput (emd_owner); + if (emd_owner->i_ino != inode->u.umsdos_i.i_emd_owner){ + printk ("UMSDOS: *** EMD_OWNER ??? *** ino = %ld %ld <> %ld " + ,inode->i_ino,emd_owner->i_ino,inode->u.umsdos_i.i_emd_owner); + } + } +} +/* + Get the inode of the directory which owns this inode. + Return 0 if ok, -EIO if error. +*/ +int umsdos_get_dirowner( + struct inode *inode, + struct inode **result) /* Hold NULL if any error */ + /* else, the inode of the directory */ +{ + int ret = -EIO; + unsigned long ino = inode->u.umsdos_i.i_dir_owner; + *result = NULL; + if (ino == 0){ + printk ("UMSDOS: umsdos_get_dirowner ino == 0\n"); + }else{ + struct inode *dir = *result = iget(inode->i_sb,ino); + if (dir != NULL){ + umsdos_patch_inode (dir,NULL,0); + ret = 0; + } + } + return ret; +} +/* + Load an inode from disk. +*/ +void UMSDOS_read_inode(struct inode *inode) +{ + PRINTK (("read inode %x ino = %d ",inode,inode->i_ino)); + msdos_read_inode(inode); + PRINTK (("ino = %d %d\n",inode->i_ino,inode->i_count)); + if (S_ISDIR(inode->i_mode) + && (inode->u.umsdos_i.u.dir_info.creating != 0 + || inode->u.umsdos_i.u.dir_info.looking != 0 + || inode->u.umsdos_i.u.dir_info.p != NULL)){ + Printk (("read inode %d %d %p\n" + ,inode->u.umsdos_i.u.dir_info.creating + ,inode->u.umsdos_i.u.dir_info.looking + ,inode->u.umsdos_i.u.dir_info.p)); + } + /* #Specification: Inode / post initialisation + To completly initialise an inode, we need access to the owner + directory, so we can locate more info in the EMD file. This is + not available the first time the inode is access, we use + a value in the inode to tell if it has been finally initialised. + + At first, we have tried testing i_count but it was causing + problem. It is possible that two or more process use the + newly accessed inode. While the first one block during + the initialisation (probably while reading the EMD file), the + others believe all is well because i_count > 1. They go banana + with a broken inode. See umsdos_lookup_patch and umsdos_patch_inode. + */ + umsdos_patch_inode(inode,NULL,0); +} + +/* + Update the disk with the inode content +*/ +void UMSDOS_write_inode(struct inode *inode) +{ + PRINTK (("UMSDOS_write_inode emd %d\n",inode->u.umsdos_i.i_emd_owner)); + msdos_write_inode(inode); + UMSDOS_notify_change (NOTIFY_TIME,inode); +} +int UMSDOS_notify_change (int flags, struct inode *inode) +{ + int ret = 0; + if (inode->i_nlink > 0){ + /* #Specification: notify_change / i_nlink > 0 + notify change is only done for inode with nlink > 0. An inode + with nlink == 0 is no longer associated with any entry in + the EMD file, so there is nothing to update. + */ + unsigned long i_emd_owner = inode->u.umsdos_i.i_emd_owner; + if (inode == inode->i_sb->s_mounted){ + /* #Specification: root inode / attributes + I don't know yet how this should work. Normally + the attributes (permissions bits, owner, times) of + a directory are stored in the EMD file of its parent. + + One thing we could do is store the attributes of the root + inode in its own EMD file. A simple entry named "." could + be used for this special case. It would be read once + when the file system is mounted and update in + UMSDOS_notify_change() (right here). + + I am not sure of the behavior of the root inode for + a real UNIX file system. For now, this is a nop. + */ + }else if (i_emd_owner != 0xffffffff && i_emd_owner != 0){ + /* This inode is not a EMD file nor an inode used internally + by MSDOS, so we can update its status. + See emd.c + */ + struct inode *emd_owner = iget (inode->i_sb,i_emd_owner); + PRINTK (("notify change %p ",inode)); + if (emd_owner == NULL){ + printk ("UMSDOS: emd_owner = NULL ???"); + ret = -EPERM; + }else{ + struct file filp; + struct umsdos_dirent entry; + filp.f_pos = inode->u.umsdos_i.pos; + PRINTK (("pos = %d ",filp.f_pos)); + /* Read only the start of the entry since we don't touch */ + /* the name */ + ret = umsdos_emd_dir_read (emd_owner,&filp,(char*)&entry + ,UMSDOS_REC_SIZE); + if (ret == 0){ + if (flags & NOTIFY_UIDGID){ + entry.uid = inode->i_uid; + entry.gid = inode->i_gid; + /* Remove those flags msdos don't like */ + flags &= ~NOTIFY_UIDGID; + } + if (flags & NOTIFY_MODE){ + entry.mode = inode->i_mode; + flags &= ~NOTIFY_MODE; + } + if (flags & NOTIFY_TIME){ + entry.atime = inode->i_atime; + entry.mtime = inode->i_mtime; + entry.ctime = inode->i_ctime; + } + entry.nlink = inode->i_nlink; + filp.f_pos = inode->u.umsdos_i.pos; + ret = umsdos_emd_dir_write (emd_owner,&filp,(char*)&entry + ,UMSDOS_REC_SIZE); + PRINTK (("notify pos %d ret %d nlink %d " + ,inode->u.umsdos_i.pos + ,ret,entry.nlink)); + /* #Specification: notify_change / msdos fs + notify_change operation are done only on the + EMD file. The msdos fs is not even called. + */ + #if 0 + if (ret == 0 + && (S_ISDIR(inode->i_mode) + || S_ISREG(inode->i_mode))){ + ret = msdos_notify_change(flags, inode); + printk ("msdos_notify %x %d",inode,ret); + } + #endif + } + iput (emd_owner); + } + PRINTK (("\n")); + } + } + return ret; +} + +/* #Specification: function name / convention + A simple convention for function name has been used in + the UMSDOS file system. First all function use the prefix + umsdos_ to avoid name clash with other part of the kernel. + + And standard VFS entry point use the prefix UMSDOS (upper case) + so it's easier to tell them apart. +*/ + +static struct super_operations umsdos_sops = { + UMSDOS_read_inode, + UMSDOS_notify_change, + UMSDOS_write_inode, + UMSDOS_put_inode, + UMSDOS_put_super, + NULL, /* added in 0.96c */ + UMSDOS_statfs, + NULL +}; + +/* + Read the super block of an Extended MS-DOS FS. +*/ +struct super_block *UMSDOS_read_super( + struct super_block *s, + void *data, + int silent) +{ + /* #Specification: mount / options + Umsdos run on top of msdos. Currently, it supports no + mount option, but happily pass all option received to + the msdos driver. I am not sure if all msdos mount option + make sens with Umsdos. Here are at least those who + are useful. + uid= + gid= + + These options affect the operation of umsdos in directories + which do not have an EMD file. They behave like normal + msdos directory, with all limitation of msdos. + */ + struct super_block *sb = msdos_read_super(s,data,silent); + printk ("UMSDOS Alpha 0.4 (compatibility level %d.%d)\n" + ,UMSDOS_VERSION,UMSDOS_RELEASE); + if (sb != NULL){ + sb->s_op = &umsdos_sops; + PRINTK (("umsdos_read_super %p\n",sb->s_mounted)); + umsdos_setup_dir_inode (sb->s_mounted); + PRINTK (("End umsdos_read_super\n")); + if (s == super_blocks){ + /* #Specification: pseudo root / mount + When a umsdos fs is mounted, a special handling is done + if it is the root partition. We check for the presence + of the file /linux/etc/init or /linux/etc/rc. + If one is there, we do a chroot("/linux"). + + We check both because (see init/main.c) the kernel + try to exec init at different place and if it fails + it tries /bin/sh /etc/rc. To be consistent with + init/main.c, many more test would have to be done + to locate init. Any complain ? + + The chroot is done manually in init/main.c but the + info (the inode) is located at mount time and store + in a global variable (pseudo_root) which is used at + different place in the umsdos driver. There is no + need to store this variable elsewhere because it + will always be one, not one per mount. + + This feature allows the installation + of a linux system within a DOS system in a subdirectory. + + A user may install its linux stuff in c:\linux + avoiding any clash with existing DOS file and subdirectory. + When linux boots, it hides this fact, showing a normal + root directory with /etc /bin /tmp ... + + The word "linux" is hardcoded in /usr/include/linux/umsdos_fs.h + in the macro UMSDOS_PSDROOT_NAME. + */ + + struct inode *pseudo; + Printk (("Mounting root\n")); + if (umsdos_real_lookup (sb->s_mounted,UMSDOS_PSDROOT_NAME + ,UMSDOS_PSDROOT_LEN,&pseudo)==0 + && S_ISDIR(pseudo->i_mode)){ + struct inode *etc = NULL; + struct inode *rc = NULL; + Printk (("/%s is there\n",UMSDOS_PSDROOT_NAME)); + if (umsdos_real_lookup (pseudo,"etc",3,&etc)==0 + && S_ISDIR(etc->i_mode)){ + struct inode *init; + Printk (("/%s/etc is there\n",UMSDOS_PSDROOT_NAME)); + if ((umsdos_real_lookup (etc,"init",4,&init)==0 + && S_ISREG(init->i_mode)) + || (umsdos_real_lookup (etc,"rc",2,&rc)==0 + && S_ISREG(rc->i_mode))){ + umsdos_setup_dir_inode (pseudo); + Printk (("Activating pseudo root /%s\n",UMSDOS_PSDROOT_NAME)); + pseudo_root = pseudo; + pseudo->i_count++; + pseudo = NULL; + } + iput (init); + iput (rc); + } + iput (etc); + } + iput (pseudo); + } + } + return sb; +} + + diff -u --recursive --new-file v1.1.34/linux/fs/umsdos/ioctl.c linux/fs/umsdos/ioctl.c --- v1.1.34/linux/fs/umsdos/ioctl.c Thu Jan 1 02:00:00 1970 +++ linux/fs/umsdos/ioctl.c Sun Jul 24 17:39:45 1994 @@ -0,0 +1,259 @@ +/* + * linux/fs/umsdos/ioctl.c + * + * Written 1993 by Jacques Gelinas + * + * Extended MS-DOS ioctl directory handling functions + */ +#include +#include +#include +#include +#include +#include +#include + +#define PRINTK(x) +#define Printk(x) printk x + +/* + Perform special function on a directory +*/ +int UMSDOS_ioctl_dir ( + struct inode *dir, + struct file *filp, + unsigned int cmd, + unsigned long data) +{ + int ret = -EPERM; + /* #Specification: ioctl / acces + Only root (effective id) is allowed to do IOCTL on directory + in UMSDOS. EPERM is returned for other user. + */ + if (current->euid == 0 + || cmd == UMSDOS_GETVERSION){ + struct umsdos_ioctl *idata = (struct umsdos_ioctl *)data; + ret = -EINVAL; + /* #Specification: ioctl / prototypes + The official prototype for the umsdos ioctl on directory + is: + + int ioctl ( + int fd, // File handle of the directory + int cmd, // command + struct umsdos_ioctl *data) + + The struct and the commands are defined in linux/umsdos_fs.h. + + umsdos_progs/umsdosio.c provide an interface in C++ to all + these ioctl. umsdos_progs/udosctl is a small utility showing + all this. + + These ioctl generally allow one to work on the EMD or the + DOS directory independantly. These are essential to implement + the synchroniser. + */ + PRINTK (("ioctl %d ",cmd)); + if (cmd == UMSDOS_GETVERSION){ + /* #Specification: ioctl / UMSDOS_GETVERSION + The field version and release of the structure + umsdos_ioctl are filled with the version and release + number of the fs code in the kernel. This will allow + some form of checking. Users won't be able to run + incompatible utility such as the synchroniser (umssync). + umsdos_progs/umsdosio.c enforce this checking. + + Return always 0. + */ + put_fs_byte (UMSDOS_VERSION,&idata->version); + put_fs_byte (UMSDOS_RELEASE,&idata->release); + ret = 0; + }else if (cmd == UMSDOS_READDIR_DOS){ + /* #Specification: ioctl / UMSDOS_READDIR_DOS + One entry is read from the DOS directory at the current + file position. The entry is put as is in the dos_dirent + field of struct umsdos_ioctl. + + Return > 0 if success. + */ + ret = msdos_readdir(dir,filp,&idata->dos_dirent,1); + }else if (cmd == UMSDOS_READDIR_EMD){ + /* #Specification: ioctl / UMSDOS_READDIR_EMD + One entry is read from the EMD at the current + file position. The entry is put as is in the umsdos_dirent + field of struct umsdos_ioctl. The corresponding mangled + DOS entry name is put in the dos_dirent field. + + All entries are read including hidden links. Blank + entries are skipped. + + Return > 0 if success. + */ + struct inode *emd_dir = umsdos_emd_dir_lookup (dir,0); + if (emd_dir != NULL){ + while (1){ + if (filp->f_pos >= emd_dir->i_size){ + ret = 0; + break; + }else{ + struct umsdos_dirent entry; + off_t f_pos = filp->f_pos; + ret = umsdos_emd_dir_readentry (emd_dir,filp,&entry); + if (ret < 0){ + break; + }else if (entry.name_len > 0){ + struct umsdos_info info; + ret = entry.name_len; + umsdos_parse (entry.name,entry.name_len,&info); + info.f_pos = f_pos; + umsdos_manglename(&info); + memcpy_tofs(&idata->umsdos_dirent,&entry + ,sizeof(entry)); + memcpy_tofs(&idata->dos_dirent.d_name + ,info.fake.fname,info.fake.len+1); + break; + } + } + } + iput (emd_dir); + }else{ + /* The absence of the EMD is simply seen as an EOF */ + ret = 0; + } + }else if (cmd == UMSDOS_INIT_EMD){ + /* #Specification: ioctl / UMSDOS_INIT_EMD + The UMSDOS_INIT_EMD command make sure the EMD + exist for a directory. If it does not, it is + created. Also, it makes sure the directory functions + table (struct inode_operations) is set to the UMSDOS + semantic. This mean that umssync may be applied to + an "opened" msdos directory, and it will change behavior + on the fly. + + Return 0 if success. + */ + extern struct inode_operations umsdos_rdir_inode_operations; + struct inode *emd_dir = umsdos_emd_dir_lookup (dir,1); + ret = emd_dir != NULL; + iput (emd_dir); + + dir->i_op = ret + ? &umsdos_dir_inode_operations + : &umsdos_rdir_inode_operations; + }else{ + struct umsdos_ioctl data; + memcpy_fromfs (&data,idata,sizeof(data)); + if (cmd == UMSDOS_CREAT_EMD){ + /* #Specification: ioctl / UMSDOS_CREAT_EMD + The umsdos_dirent field of the struct umsdos_ioctl is used + as is to create a new entry in the EMD of the directory. + The DOS directory is not modified. + No validation is done (yet). + + Return 0 if success. + */ + struct umsdos_info info; + /* This makes sure info.entry and info in general is correctly */ + /* initialised */ + memcpy (&info.entry,&data.umsdos_dirent + ,sizeof(data.umsdos_dirent)); + umsdos_parse (data.umsdos_dirent.name + ,data.umsdos_dirent.name_len,&info); + ret = umsdos_newentry (dir,&info); + }else if (cmd == UMSDOS_UNLINK_EMD){ + /* #Specification: ioctl / UMSDOS_UNLINK_EMD + The umsdos_dirent field of the struct umsdos_ioctl is used + as is to remove an entry from the EMD of the directory. + No validation is done (yet). The mode field is used + to validate S_ISDIR or S_ISREG. + + Return 0 if success. + */ + struct umsdos_info info; + /* This makes sure info.entry and info in general is correctly */ + /* initialised */ + memcpy (&info.entry,&data.umsdos_dirent + ,sizeof(data.umsdos_dirent)); + umsdos_parse (data.umsdos_dirent.name + ,data.umsdos_dirent.name_len,&info); + ret = umsdos_delentry (dir,&info + ,S_ISDIR(data.umsdos_dirent.mode)); + }else if (cmd == UMSDOS_UNLINK_DOS){ + /* #Specification: ioctl / UMSDOS_UNLINK_DOS + The dos_dirent field of the struct umsdos_ioctl is used to + execute a msdos_unlink operation. The d_name and d_reclen + fields are used. + + Return 0 if success. + */ + dir->i_count++; + ret = msdos_unlink (dir,data.dos_dirent.d_name + ,data.dos_dirent.d_reclen); + }else if (cmd == UMSDOS_RMDIR_DOS){ + /* #Specification: ioctl / UMSDOS_RMDIR_DOS + The dos_dirent field of the struct umsdos_ioctl is used to + execute a msdos_unlink operation. The d_name and d_reclen + fields are used. + + Return 0 if success. + */ + dir->i_count++; + ret = msdos_rmdir (dir,data.dos_dirent.d_name + ,data.dos_dirent.d_reclen); + }else if (cmd == UMSDOS_STAT_DOS){ + /* #Specification: ioctl / UMSDOS_STAT_DOS + The dos_dirent field of the struct umsdos_ioctl is + used to execute a stat operation in the DOS directory. + The d_name and d_reclen fields are used. + + The following field of umsdos_ioctl.stat are filled. + + st_ino,st_mode,st_size,st_atime,st_mtime,st_ctime, + Return 0 if success. + */ + struct inode *inode; + ret = umsdos_real_lookup (dir,data.dos_dirent.d_name + ,data.dos_dirent.d_reclen,&inode); + if (ret == 0){ + data.stat.st_ino = inode->i_ino; + data.stat.st_mode = inode->i_mode; + data.stat.st_size = inode->i_size; + data.stat.st_atime = inode->i_atime; + data.stat.st_ctime = inode->i_ctime; + data.stat.st_mtime = inode->i_mtime; + memcpy_tofs (&idata->stat,&data.stat,sizeof(data.stat)); + iput (inode); + } + }else if (cmd == UMSDOS_DOS_SETUP){ + /* #Specification: ioctl / UMSDOS_DOS_SETUP + The UMSDOS_DOS_SETUP ioctl allow changing the + default permission of the MsDOS file system driver + on the fly. The MsDOS driver apply global permission + to every file and directory. Normally these permissions + are controlled by a mount option. This is not + available for root partition, so a special utility + (umssetup) is provided to do this, normally in + /etc/rc.local. + + Be aware that this apply ONLY to MsDOS directory + (those without EMD --linux-.---). Umsdos directory + have independant (standard) permission for each + and every file. + + The field umsdos_dirent provide the information needed. + umsdos_dirent.uid and gid sets the owner and group. + umsdos_dirent.mode set the permissions flags. + */ + dir->i_sb->u.msdos_sb.fs_uid = data.umsdos_dirent.uid; + dir->i_sb->u.msdos_sb.fs_gid = data.umsdos_dirent.gid; + dir->i_sb->u.msdos_sb.fs_umask = data.umsdos_dirent.mode; + ret = 0; + } + } + } + PRINTK (("ioctl return %d\n",ret)); + return ret; +} + + + diff -u --recursive --new-file v1.1.34/linux/fs/umsdos/mangle.c linux/fs/umsdos/mangle.c --- v1.1.34/linux/fs/umsdos/mangle.c Thu Jan 1 02:00:00 1970 +++ linux/fs/umsdos/mangle.c Sun Jul 24 17:39:45 1994 @@ -0,0 +1,478 @@ +/* + * linux/fs/umsdos/mangle.c + * + * Written 1993 by Jacques Gelinas + * + * Control the mangling of file name to fit msdos name space. + * Many optimisation by GLU == dglaude@is1.vub.ac.be (GLAUDE DAVID) +*/ +#include +#include +#include +#include +#include + +/* + Complete the mangling of the MSDOS fake name + based on the position of the entry in the EMD file. + + Simply complete the job of umsdos_parse; fill the extension. + + Beware that info->f_pos must be set. +*/ +void umsdos_manglename (struct umsdos_info *info) +{ + if (info->msdos_reject){ + /* #Specification: file name / non MSDOS conforming / mangling + Each non MSDOS conforming file has a special extension + build from the entry position in the EMD file. + + This number is then transform in a base 32 number, where + each digit is expressed like hexadecimal number, using + digit and letter, except it uses 22 letters from 'a' to 'v'. + The number 32 comes from 2**5. It is faster to split a binary + number using a base which is a power of two. And I was 32 + when I started this project. Pick your answer :-) . + + If the result is '0', it is replace with '_', simply + to make it odd. + + This is true for the first two character of the extension. + The last one is taken from a list of odd character, which + are: + + { } ( ) ! ` ^ & @ + + With this scheme, we can produce 9216 ( 9* 32 * 32) + different extensions which should not clash with any useful + extension already popular or meaningful. Since most directory + have much less than 32 * 32 files in it, the first character + of the extension of any mangle name will be {. + + Here are the reason to do this (this kind of mangling). + + -The mangling is deterministic. Just by the extension, we + are able to locate the entry in the EMD file. + + -By keeping to beginning of the file name almost unchange, + we are helping the MSDOS user. + + -The mangling produces names not too ugly, so an msdos user + may live with it (remember it, type it, etc...). + + -The mangling produces names ugly enough so no one will + ever think of using such a name in real life. This is not + fool proof. I don't think there is a total solution to this. + */ + union { + int entry_num; + struct { + unsigned num1:5,num2:5,num3:5; + }num; + } u; + char *pt = info->fake.fname + info->fake.len; + /* lookup for encoding the last character of the extension */ + /* It contain valid character after the ugly one to make sure */ + /* even if someone overflow the 32 * 32 * 9 limit, it still do */ + /* something */ + #define SPECIAL_MANGLING '{','}','(',')','!','`','^','&','@' + static char lookup3[]={ + SPECIAL_MANGLING, + /* This is the start of lookup12 */ + '_','1','2','3','4','5','6','7','8','9', + 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o', + 'p','q','r','s','t','u','v' + }; + #define lookup12 (lookup3+9) + u.entry_num = info->f_pos / UMSDOS_REC_SIZE; + if (u.entry_num > (9* 32 * 32)){ + printk ("UMSDOS: More than 9216 file in a directory.\n" + "This may break the mangling strategy.\n" + "Not a killer problem. See doc.\n"); + } + *pt++ = '.'; + *pt++ = lookup3 [u.num.num3]; + *pt++ = lookup12[u.num.num2]; + *pt++ = lookup12[u.num.num1]; + *pt = '\0'; /* help doing printk */ + info->fake.len += 4; + info->msdos_reject = 0; /* Avoid mangling twice */ + } +} + +/* + Evaluate the record size needed to store of name of len character. + The value returned is a multiple of UMSDOS_REC_SIZE. +*/ +int umsdos_evalrecsize (int len) +{ + struct umsdos_dirent dirent; + int nbrec = 1+((len-1+(dirent.name-(char*)&dirent)) + / UMSDOS_REC_SIZE); + return nbrec * UMSDOS_REC_SIZE; + /* + GLU This should be inlined or something to speed it up to the max. + GLU nbrec is absolutely not needed to return the value. + */ +} +#ifdef TEST +int umsdos_evalrecsize_old (int len) +{ + struct umsdos_dirent dirent; + int size = len + (dirent.name-(char*)&dirent); + int nbrec = size / UMSDOS_REC_SIZE; + int extra = size % UMSDOS_REC_SIZE; + if (extra > 0) nbrec++; + return nbrec * UMSDOS_REC_SIZE; +} +#endif +/* + Fill the struct info with the full and msdos name of a file + Return 0 if all is ok, a negative error code otherwise. +*/ +int umsdos_parse ( + const char *fname, + int len, + struct umsdos_info *info) +{ + int ret = -ENAMETOOLONG; + /* #Specification: file name / too long + If a file name exceed UMSDOS maxima, the file name is silently + truncated. This makes it conformant with the other file system + of Linux (minix and ext2 at least). + */ + if (len > UMSDOS_MAXNAME) len = UMSDOS_MAXNAME; + { + const char *firstpt=NULL; /* First place we saw a . in fname */ + /* #Specification: file name / non MSDOS conforming / base length 0 + file name beginning with a period '.' are invalid for MsDOS. + It needs absolutly a base name. So the file name is mangled + */ + int ivldchar = fname[0] == '.';/* At least one invalid character */ + int msdos_len = len; + int base_len; + /* + cardinal_per_size tells if there exist at least one + DOS pseudo devices on length n. See the test below. + */ + static const char cardinal_per_size[9]={ + 0, 0, 0, 1, 1, 0, 1, 0, 1 + }; + /* + lkp translate all character to acceptable character (for DOS). + When lkp[n] == n, it means also it is an acceptable one. + So it serve both as a flag and as a translator. + */ + static char lkp[256]; + static char is_init=0; + if (!is_init){ + /* + Initialisation of the array is easier and less error prone + like this. + */ + int i; + static char *spc = "\"*+,/:;<=>?[\\]|~"; + is_init = 1; + for (i=0; i<=32; i++) lkp[i] = '#'; + for (i=33; i<'A'; i++) lkp[i] = (char)i; + for (i='A'; i<='Z'; i++) lkp[i] = (char)(i+('a'-'A')); + for (i='Z'+1; i<127; i++) lkp[i] = (char)i; + for (i=128; i<256; i++) lkp[i] = '#'; + + lkp['.'] = '_'; + while (*spc != '\0') lkp[(unsigned char)(*spc++)] = '#'; + } + /* GLU + file name wich are longer than 8+'.'+3 are invalid for MsDOS. + So the file name is to be mangled no more test needed. + This Speed Up for long and very long name. + The position of the last point is no more necessary anyway. + */ + if (len<=(8+1+3)){ + const char *pt = fname; + const char *endpt = fname + len; + while (pt < endpt){ + if (*pt == '.'){ + if (firstpt != NULL){ + /* 2 . in a file name. Reject */ + ivldchar = 1; + break; + }else{ + int extlen = (int)(endpt - pt); + firstpt = pt; + if (firstpt - fname > 8){ + /* base name longer than 8: reject */ + ivldchar = 1; + break; + }else if (extlen > 4){ + /* Extension longer than 4 (including .): reject */ + ivldchar = 1; + break; + }else if (extlen == 1){ + /* #Specification: file name / non MSDOS conforming / last char == . + If the last character of a file name is + a period, mangling is applied. MsDOS do + not support those file name. + */ + ivldchar = 1; + break; + }else if (extlen == 4){ + /* #Specification: file name / non MSDOS conforming / mangling clash + To avoid clash with the umsdos mangling, any file + with a special character as the first character + of the extension will be mangled. This solve the + following problem: + + touch FILE + # FILE is invalid for DOS, so mangling is applied + # file.{_1 is created in the DOS directory + touch file.{_1 + # To UMSDOS file point to a single DOS entry. + # So file.{_1 has to be mangled. + */ + static char special[]={ + SPECIAL_MANGLING,'\0' + }; + if (strchr(special,firstpt[1])!= NULL){ + ivldchar = 1; + break; + } + } + } + }else if (lkp[(unsigned char)(*pt)] != *pt){ + ivldchar = 1; + break; + } + pt++; + } + }else{ + ivldchar = 1; + } + if (ivldchar + || (firstpt == NULL && len > 8) + || (len == UMSDOS_EMD_NAMELEN + && memcmp(fname,UMSDOS_EMD_FILE,UMSDOS_EMD_NAMELEN)==0)){ + /* #Specification: file name / --linux-.--- + The name of the EMD file --linux-.--- is map to a mangled + name. So UMSDOS does not restrict its use. + */ + /* #Specification: file name / non MSDOS conforming / mangling + Non MSDOS conforming file name must use some alias to fit + in the MSDOS name space. + + The strategy is simple. The name is simply truncated to + 8 char. points are replace with underscore and a + number is given as an extension. This number correspond + to the entry number in the EMD file. The EMD file + only need to carry the real name. + + Upper case is also convert to lower case. + Control character are converted to #. + Space are converted to #. + The following character are also converted to #. + " * + , / : ; < = > ? [ \ ] | ~ + + Sometime, the problem is not in MsDOS itself but in + command.com. + */ + int i; + char *pt = info->fake.fname; + base_len = msdos_len = (msdos_len>8) ? 8 : msdos_len; + /* + There is no '.' any more so we know for a fact that + the base lenght is the lenght. + */ + memcpy (info->fake.fname,fname,msdos_len); + for (i=0; imsdos_reject = 1; + /* + The numeric extension is added only when we know + the position in the EMD file, in umsdos_newentry(), + umsdos_delentry(), and umsdos_findentry(). + See umsdos_manglename(). + */ + }else{ + /* Conforming MSDOS file name */ + strcpy (info->fake.fname,fname); /* GLU C'est sur on a un 0 a la fin */ + info->msdos_reject = 0; + base_len = firstpt != NULL ? (int)(firstpt - fname) : len; + } + if (cardinal_per_size[base_len]){ + /* #Specification: file name / MSDOS devices / mangling + To avoid unreachable file from MsDOS, any MsDOS conforming + file with a basename equal to one of the MsDOS pseudo + devices will be mangled. + + If a file such as "prn" was created, it would be unreachable + under MsDOS because prn is assumed to be the printer, even + if the file does have an extension. + + Since the extension is unimportant to MsDOS, we must patch + the basename also. We simply insert a minus '-'. To avoid + conflict with valid file with a minus in front (such as + "-prn"), we add an mangled extension like any other + mangled file name. + + Here is the list of DOS pseudo devices: + + "prn","con","aux","nul", + "lpt1","lpt2","lpt3","lpt4", + "com1","com2","com3","com4", + "clock$" + + and some standard ones for common DOS programs + + "emmxxxx0","xmsxxxx0","setverxx" + + (Thanks to Chris Hall + for pointing these to me). + + Is there one missing ? + */ + /* This table must be ordered by length */ + static const char *tbdev[]={ + "prn","con","aux","nul", + "lpt1","lpt2","lpt3","lpt4", + "com1","com2","com3","com4", + "clock$", + "emmxxxx0","xmsxxxx0","setverxx" + }; + /* Tell where to find in tbdev[], the first name of */ + /* a certain length */ + static const char start_ind_dev[9]={ + 0, 0, 0, 4, 12, 12, 13, 13, 16 + }; + char basen[9]; + int i; + for (i=start_ind_dev[base_len-1]; ifake.fname,tbdev[i],base_len)==0){ + memcpy (basen,info->fake.fname,base_len); + basen[base_len] = '\0'; /* GLU C'est sur on a un 0 a la fin */ + /* + GLU On ne fait cela que si necessaire, on essaye d'etre le + GLU simple dans le cas general (le plus frequent). + */ + info->fake.fname[0] = '-'; + strcpy (info->fake.fname+1,basen); /* GLU C'est sur on a un 0 a la fin */ + msdos_len = (base_len==8) ? 8 : base_len + 1; + info->msdos_reject = 1; + break; + } + } + } + info->fake.fname[msdos_len] = '\0'; /* Help doing printk */ + /* GLU Ce zero devrais deja y etre ! (invariant ?) */ + info->fake.len = msdos_len; + /* Pourquoi ne pas utiliser info->fake.len partout ??? plus long ?*/ + memcpy (info->entry.name,fname,len); + info->entry.name_len = len; + ret = 0; + } + /* + Evaluate how many record are needed to store this entry. + */ + info->recsize = umsdos_evalrecsize (len); + return ret; +} + +#ifdef TEST + +struct MANG_TEST{ + char *fname; /* Name to validate */ + int msdos_reject; /* Expected msdos_reject flag */ + char *msname; /* Expected msdos name */ +}; + +struct MANG_TEST tb[]={ + "hello", 0, "hello", + "hello.1", 0, "hello.1", + "hello.1_", 0, "hello.1_", + "prm", 0, "prm", + +#ifdef PROPOSITION + "HELLO", 1, "hello", + "Hello.1", 1, "hello.1", + "Hello.c", 1, "hello.c", +#elseif +/* + Je trouve les trois exemples ci-dessous tres "malheureux". + Je propose de mettre en minuscule dans un passe preliminaire, + et de tester apres si il y a d'autres caracters "mechants". + Bon, je ne l'ai pas fait, parceque ce n'est pas si facilement + modifiable que ca. Mais c'est pour le principe. + Evidemment cela augmente les chances de "Colision", + par exemple: entre "HELLO" et "Hello", mais ces problemes + peuvent etre traiter ailleur avec les autres colisions. +*/ + "HELLO", 1, "hello", + "Hello.1", 1, "hello_1", + "Hello.c", 1, "hello_c", +#endif + + "hello.{_1", 1, "hello_{_", + "hello\t", 1, "hello#", + "hello.1.1", 1, "hello_1_", + "hel,lo", 1, "hel#lo", + "Salut.Tu.vas.bien?", 1, "salut_tu", + ".profile", 1, "_profile", + ".xv", 1, "_xv", + "toto.", 1, "toto_", + "clock$.x", 1, "-clock$", + "emmxxxx0", 1, "-emmxxxx", + "emmxxxx0.abcd", 1, "-emmxxxx", + "aux", 1, "-aux", + "prn", 1, "-prn", + "prn.abc", 1, "-prn", + "PRN", 1, "-prn", +/* +GLU ATTENTION : Le resultat de ceux-ci sont differents avec ma version +GLU du mangle par rapport au mangle originale. +GLU CAUSE: La maniere de calculer la variable baselen. +GLU Pour toi c'est toujours 3 +GLU Pour moi c'est respectivement 7, 8 et 8 +*/ + "PRN.abc", 1, "prn_abc", + "Prn.abcd", 1, "prn_abcd", + "prn.abcd", 1, "prn_abcd", + "Prn.abcdefghij", 1, "prn_abcd" +}; + +int main (int argc, char *argv[]) +{ + int i,rold,rnew; + printf ("Testing the umsdos_parse.\n"); + for (i=0; ifname,strlen(pttb->fname),&info); + if (strcmp(info.fake.fname,pttb->msname)!=0){ + printf ("**** %s -> ",pttb->fname); + printf ("%s <> %s\n",info.fake.fname,pttb->msname); + }else if (info.msdos_reject != pttb->msdos_reject){ + printf ("**** %s -> %s ",pttb->fname,pttb->msname); + printf ("%d <> %d\n",info.msdos_reject,pttb->msdos_reject); + }else{ + printf (" %s -> %s %d\n",pttb->fname,pttb->msname + ,pttb->msdos_reject); + } + } + printf ("Testing the new umsdos_evalrecsize."); + for (i=0; i +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PRINTK(x) +#define Printk(x) printk x + +#if 1 +/* + Wait for creation exclusivity. + Return 0 if the dir was already available. + Return 1 if a wait was necessary. + When 1 is return, it means a wait was done. It does not + mean the directory is available. +*/ +static int umsdos_waitcreate(struct inode *dir) +{ + int ret = 0; + if (dir->u.umsdos_i.u.dir_info.creating + && dir->u.umsdos_i.u.dir_info.pid != current->pid){ + sleep_on(&dir->u.umsdos_i.u.dir_info.p); + ret = 1; + } + return ret; +} +/* + Wait for any lookup process to finish +*/ +static void umsdos_waitlookup (struct inode *dir) +{ + while (dir->u.umsdos_i.u.dir_info.looking){ + sleep_on(&dir->u.umsdos_i.u.dir_info.p); + } +} +/* + Lock all other process out of this directory. +*/ +void umsdos_lockcreate (struct inode *dir) +{ + /* #Specification: file creation / not atomic + File creation is a two step process. First we create (allocate) + an entry in the EMD file and then (using the entry offset) we + build a unique name for MSDOS. We create this name in the msdos + space. + + We have to use semaphore (sleep_on/wake_up) to prevent lookup + into a directory when we create a file or directory and to + prevent creation while a lookup is going on. Since many lookup + may happen at the same time, the semaphore is a counter. + + Only one creation is allowed at the same time. This protection + may not be necessary. The problem arise mainly when a lookup + or a readdir is done while a file is partially created. The + lookup process see that as a "normal" problem and silently + erase the file from the EMD file. Normal because a file + may be erased during a MSDOS session, but not removed from + the EMD file. + + The locking is done on a directory per directory basis. Each + directory inode has its wait_queue. + + For some operation like hard link, things even get worse. Many + creation must occur at once (atomic). To simplify the design + a process is allowed to recursivly lock the directory for + creation. The pid of the locking process is kept along with + a counter so a second level of locking is granted or not. + */ + /* + Wait for any creation process to finish except + if we (the process) own the lock + */ + while (umsdos_waitcreate(dir)!=0); + dir->u.umsdos_i.u.dir_info.creating++; + dir->u.umsdos_i.u.dir_info.pid = current->pid; + umsdos_waitlookup (dir); +} +/* + Lock all other process out of those two directories. +*/ +static void umsdos_lockcreate2 (struct inode *dir1, struct inode *dir2) +{ + /* + We must check that both directory are available before + locking anyone of them. This is to avoid some deadlock. + Thanks to dglaude@is1.vub.ac.be (GLAUDE DAVID) for pointing + this to me. + */ + while (1){ + if (umsdos_waitcreate(dir1)==0 + && umsdos_waitcreate(dir2)==0){ + /* We own both now */ + dir1->u.umsdos_i.u.dir_info.creating++; + dir1->u.umsdos_i.u.dir_info.pid = current->pid; + dir2->u.umsdos_i.u.dir_info.creating++; + dir2->u.umsdos_i.u.dir_info.pid = current->pid; + break; + } + } + umsdos_waitlookup(dir1); + umsdos_waitlookup(dir2); +} +/* + Wait until creation is finish in this directory. +*/ +void umsdos_startlookup (struct inode *dir) +{ + while (umsdos_waitcreate (dir) != 0); + dir->u.umsdos_i.u.dir_info.looking++; +} +void check_page_tables(void); + +/* + Unlock the directory. +*/ +void umsdos_unlockcreate (struct inode *dir) +{ + dir->u.umsdos_i.u.dir_info.creating--; + if (dir->u.umsdos_i.u.dir_info.creating < 0){ + printk ("UMSDOS: dir->u.umsdos_i.u.dir_info.creating < 0: %d" + ,dir->u.umsdos_i.u.dir_info.creating); + } + wake_up (&dir->u.umsdos_i.u.dir_info.p); +} +/* + Tell directory lookup is over. +*/ +void umsdos_endlookup (struct inode *dir) +{ + dir->u.umsdos_i.u.dir_info.looking--; + if (dir->u.umsdos_i.u.dir_info.looking < 0){ + printk ("UMSDOS: dir->u.umsdos_i.u.dir_info.looking < 0: %d" + ,dir->u.umsdos_i.u.dir_info.looking); + } + wake_up (&dir->u.umsdos_i.u.dir_info.p); +} +#else +static void umsdos_lockcreate (struct inode *dir){} +static void umsdos_lockcreate2 (struct inode *dir1, struct inode *dir2){} +void umsdos_startlookup (struct inode *dir){} +static void umsdos_unlockcreate (struct inode *dir){} +void umsdos_endlookup (struct inode *dir){} +#endif +static int umsdos_nevercreat( + struct inode *dir, + const char *name, /* Name of the file to add */ + int len, + int errcod) /* Length of the name */ +{ + int ret = 0; + if (umsdos_is_pseudodos(dir,name,len)){ + /* #Specification: pseudo root / any file creation /DOS + The pseudo sub-directory /DOS can't be created! + EEXIST is returned. + + The pseudo sub-directory /DOS can't be removed! + EPERM is returned. + */ + ret = -EPERM; + ret = errcod; + }else if (name[0] == '.' + && (len == 1 || (len == 2 && name[1] == '.'))){ + /* #Specification: create / . and .. + If one try to creates . or .., it always fail and return + EEXIST. + + If one try to delete . or .., it always fail and return + EPERM. + + This should be test at the VFS layer level to avoid + duplicating this in all file systems. Any comments ? + */ + ret = errcod; + } + return ret; +} + +/* + Add a new file (ordinary or special) into the alternate directory. + The file is added to the real MSDOS directory. If successfull, it + is then added to the EDM file. + + Return the status of the operation. 0 mean success. +*/ +static int umsdos_create_any ( + struct inode *dir, + const char *name, /* Name of the file to add */ + int len, /* Length of the name */ + int mode, /* Permission bit + file type ??? */ + int rdev, /* major, minor or 0 for ordinary file */ + /* and symlinks */ + char flags, + struct inode **result) /* Will hold the inode of the newly created */ + /* file */ +{ + int ret = umsdos_nevercreat(dir,name,len,-EEXIST); + if (ret == 0){ + struct umsdos_info info; + ret = umsdos_parse (name,len,&info); + *result = NULL; + if (ret == 0){ + info.entry.mode = mode; + info.entry.rdev = rdev; + info.entry.flags = flags; + info.entry.uid = current->euid; + info.entry.gid = (dir->i_mode & S_ISGID) + ? dir->i_gid : current->egid; + info.entry.ctime = info.entry.atime = info.entry.mtime + = CURRENT_TIME; + info.entry.nlink = 1; + umsdos_lockcreate(dir); + ret = umsdos_newentry (dir,&info); + if (ret == 0){ + dir->i_count++; + ret = msdos_create (dir,info.fake.fname,info.fake.len + ,S_IFREG|0777,result); + if (ret == 0){ + struct inode *inode = *result; + umsdos_lookup_patch (dir,inode,&info.entry,info.f_pos); + PRINTK (("inode %p[%d] ",inode,inode->i_count)); + PRINTK (("Creation OK: [%d] %s %d pos %d\n",dir->i_ino + ,info.fake.fname,current->pid,info.f_pos)); + }else{ + /* #Specification: create / file exist in DOS + Here is a situation. Trying to create a file with + UMSDOS. The file is unknown to UMSDOS but already + exist in the DOS directory. + + Here is what we are NOT doing: + + We could silently assume that everything is fine + and allows the creation to succeed. + + It is possible not all files in the partition + are mean to be visible from linux. By trying to create + those file in some directory, one user may get access + to those file without proper permissions. Looks like + a security hole to me. Off course sharing a file system + with DOS is some kind of security hole :-) + + So ? + + We return EEXIST in this case. + The same is true for directory creation. + */ + if (ret == -EEXIST){ + printk ("UMSDOS: out of sync, Creation error [%ld], " + "deleting %s %d %d pos %ld\n",dir->i_ino + ,info.fake.fname,-ret,current->pid,info.f_pos); + } + umsdos_delentry (dir,&info,0); + } + PRINTK (("umsdos_create %s ret = %d pos %d\n" + ,info.fake.fname,ret,info.f_pos)); + } + umsdos_unlockcreate(dir); + } + } + iput (dir); + return ret; +} +/* + Initialise the new_entry from the old for a rename operation. + (Only useful for umsdos_rename_f() below). +*/ +static void umsdos_ren_init( + struct umsdos_info *new_info, + struct umsdos_info *old_info, + int flags) /* 0 == copy flags from old_name */ + /* != 0, this is the value of flags */ +{ + new_info->entry.mode = old_info->entry.mode; + new_info->entry.rdev = old_info->entry.rdev; + new_info->entry.uid = old_info->entry.uid; + new_info->entry.gid = old_info->entry.gid; + new_info->entry.ctime = old_info->entry.ctime; + new_info->entry.atime = old_info->entry.atime; + new_info->entry.mtime = old_info->entry.mtime; + new_info->entry.flags = flags ? flags : old_info->entry.flags; + new_info->entry.nlink = old_info->entry.nlink; +} + +#define chkstk() \ + if (STACK_MAGIC != *(unsigned long *)current->kernel_stack_page){\ + printk(KERN_ALERT "UMSDOS: %s magic %x != %lx ligne %d\n" \ + , current->comm,STACK_MAGIC \ + ,*(unsigned long *)current->kernel_stack_page \ + ,__LINE__); \ + } + +/* + Rename a file (move) in the file system. +*/ +static int umsdos_rename_f( + struct inode * old_dir, + const char * old_name, + int old_len, + struct inode * new_dir, + const char * new_name, + int new_len, + int flags) /* 0 == copy flags from old_name */ + /* != 0, this is the value of flags */ +{ + int ret = EPERM; + struct umsdos_info old_info; + int old_ret = umsdos_parse (old_name,old_len,&old_info); + struct umsdos_info new_info; + int new_ret = umsdos_parse (new_name,new_len,&new_info); +chkstk(); + PRINTK (("umsdos_rename %d %d ",old_ret,new_ret)); + if (old_ret == 0 && new_ret == 0){ + umsdos_lockcreate2(old_dir,new_dir); +chkstk(); + PRINTK (("old findentry ")); + ret = umsdos_findentry(old_dir,&old_info,0); +chkstk(); + PRINTK (("ret %d ",ret)); + if (ret == 0){ + PRINTK (("new newentry ")); + umsdos_ren_init(&new_info,&old_info,flags); + ret = umsdos_newentry (new_dir,&new_info); +chkstk(); + PRINTK (("ret %d %d ",ret,new_info.fake.len)); + if (ret == 0){ + PRINTK (("msdos_rename ")); + old_dir->i_count++; + new_dir->i_count++; /* Both inode are needed later */ + ret = msdos_rename (old_dir + ,old_info.fake.fname,old_info.fake.len + ,new_dir + ,new_info.fake.fname,new_info.fake.len); +chkstk(); + PRINTK (("after m_rename ret %d ",ret)); + if (ret != 0){ + umsdos_delentry (new_dir,&new_info + ,S_ISDIR(new_info.entry.mode)); +chkstk(); + }else{ + ret = umsdos_delentry (old_dir,&old_info + ,S_ISDIR(old_info.entry.mode)); +chkstk(); + if (ret == 0){ + /* + This UMSDOS_lookup does not look very useful. + It makes sure that the inode of the file will + be correctly setup (umsdos_patch_inode()) in + case it is already in use. + + Not very efficient ... + */ + struct inode *inode; + new_dir->i_count++; + PRINTK (("rename lookup len %d %d -- ",new_len,new_info.entry.flags)); + ret = UMSDOS_lookup (new_dir,new_name,new_len + ,&inode); +chkstk(); + if (ret != 0){ + printk ("UMSDOS: partial rename for file %s\n" + ,new_info.entry.name); + }else{ + /* + Update f_pos so notify_change will succeed + if the file was already in use. + */ + umsdos_set_dirinfo (inode,new_dir,new_info.f_pos); +chkstk(); + iput (inode); + } + } + } + } + } + umsdos_unlockcreate(old_dir); + umsdos_unlockcreate(new_dir); + } + iput (old_dir); + iput (new_dir); + PRINTK (("\n")); + return ret; +} +/* + Setup un Symbolic link or a (pseudo) hard link + Return a negative error code or 0 if ok. +*/ +static int umsdos_symlink_x( + struct inode * dir, + const char * name, + int len, + const char * symname, /* name will point to this path */ + int mode, + char flags) +{ + /* #Specification: symbolic links / strategy + A symbolic link is simply a file which hold a path. It is + implemented as a normal MSDOS file (not very space efficient :-() + + I see 2 different way to do it. One is to place the link data + in unused entry of the EMD file. The other is to have a separate + file dedicated to hold all symbolic links data. + + Lets go for simplicity... + */ + struct inode *inode; + int ret; + dir->i_count++; /* We keep the inode in case we need it */ + /* later */ + ret = umsdos_create_any (dir,name,len,mode,0,flags,&inode); + PRINTK (("umsdos_symlink ret %d ",ret)); + if (ret == 0){ + int len = strlen(symname); + struct file filp; + filp.f_pos = 0; + /* Make the inode acceptable to MSDOS */ + ret = umsdos_file_write_kmem (inode,&filp,(char*)symname,len); + iput (inode); + if (ret >= 0){ + if (ret != len){ + ret = -EIO; + printk ("UMSDOS: " + "Can't write symbolic link data\n"); + }else{ + ret = 0; + } + } + if (ret != 0){ + UMSDOS_unlink (dir,name,len); + dir = NULL; + } + } + iput (dir); + PRINTK (("\n")); + return ret; +} +/* + Setup un Symbolic link. + Return a negative error code or 0 if ok. +*/ +int UMSDOS_symlink( + struct inode * dir, + const char * name, + int len, + const char * symname) /* name will point to this path */ +{ + return umsdos_symlink_x (dir,name,len,symname,S_IFLNK|0777,0); +} +/* + Add a link to an inode in a directory +*/ +int UMSDOS_link ( + struct inode * oldinode, + struct inode * dir, + const char * name, + int len) +{ + /* #Specification: hard link / strategy + Well ... hard link are difficult to implement on top of an + MsDOS fat file system. Unlike UNIX file systems, there are no + inode. A directory entry hold the functionnality of the inode + and the entry. + + We will used the same strategy as a normal Unix file system + (with inode) except we will do it symbolicly (using paths). + + Because anything can happen during a DOS session (defragment, + directory sorting, etc...), we can't rely on MsDOS pseudo + inode number to record the link. For this reason, the link + will be done using hidden symbolic links. The following + scenario illustrate how it work. + + Given a file /foo/file + + ln /foo/file /tmp/file2 + + become internally + + mv /foo/file /foo/-LINK1 + ln -s /foo/-LINK1 /foo/file + ln -s /foo/-LINK1 /tmp/file2 + + Using this strategy, we can operate on /foo/file or /foo/file2. + We can remove one and keep the other, like a normal Unix hard link. + We can rename /foo/file ou /tmp/file2 independantly. + + The entry -LINK1 will be hidden. It will hold a link count. + When all link are erased, the hidden file is erased too. + */ + /* #Specification: weakness / hard link + The strategy for hard link introduces a side effect that + may or may not be acceptable. Here is the sequence + + mkdir subdir1 + touch subdir1/file + mkdir subdir2 + ln subdir1/file subdir2/file + rm subdir1/file + rmdir subdir1 + rmdir: subdir1: Directory not empty + + This happen because there is an invisible file (--link) in + subdir1 which is referenced by subdir2/file. + + Any idea ? + */ + /* #Specification: weakness / hard link / rename directory + Another weakness of hard link come from the fact that + it is based on hidden symbolic links. Here is an example. + + mkdir /subdir1 + touch /subdir1/file + mkdir /subdir2 + ln /subdir1/file subdir2/file + mv /subdir1 subdir3 + ls -l /subdir2/file + + Since /subdir2/file is a hidden symbolic link + to /subdir1/..hlinkNNN, accessing it will fail since + /subdir1 does not exist anymore (has been renamed). + */ + int ret = 0; + if (S_ISDIR(oldinode->i_mode)){ + /* #Specification: hard link / directory + A hard link can't be made on a directory. EPERM is returned + in this case. + */ + ret = -EPERM; + }else if ((ret = umsdos_nevercreat(dir,name,len,-EPERM))==0){ + struct inode *olddir; + ret = umsdos_get_dirowner(oldinode,&olddir); + PRINTK (("umsdos_link dir_owner = %d -> %p [%d] " + ,oldinode->u.umsdos_i.i_dir_owner,olddir,olddir->i_count)); + if (ret == 0){ + struct umsdos_dirent entry; + umsdos_lockcreate2(dir,olddir); + ret = umsdos_inode2entry (olddir,oldinode,&entry); + if (ret == 0){ + PRINTK (("umsdos_link :%s: ino %d flags %d " + ,entry.name + ,oldinode->i_ino,entry.flags)); + if (!(entry.flags & UMSDOS_HIDDEN)){ + /* #Specification: hard link / first hard link + The first time a hard link is done on a file, this + file must be renamed and hidden. Then an internal + simbolic link must be done on the hidden file. + + The second link is done after on this hidden file. + + It is expected that the Linux MSDOS file system + keeps the same pseudo inode when a rename operation + is done on a file in the same directory. + */ + struct umsdos_info info; + ret = umsdos_newhidden (olddir,&info); + if (ret == 0){ + olddir->i_count+=2; + PRINTK (("olddir[%d] ",olddir->i_count)); + ret = umsdos_rename_f (olddir,entry.name + ,entry.name_len + ,olddir,info.entry.name,info.entry.name_len + ,UMSDOS_HIDDEN); + if (ret == 0){ + char *path = (char*)kmalloc(PATH_MAX,GFP_KERNEL); + if (path == NULL){ + ret = -ENOMEM; + }else{ + PRINTK (("olddir[%d] ",olddir->i_count)); + ret = umsdos_locate_path (oldinode,path); + PRINTK (("olddir[%d] ",olddir->i_count)); + if (ret == 0){ + olddir->i_count++; + ret = umsdos_symlink_x (olddir + ,entry.name + ,entry.name_len,path + ,S_IFREG|0777,UMSDOS_HLINK); + if (ret == 0){ + dir->i_count++; + ret = umsdos_symlink_x (dir,name,len + ,path + ,S_IFREG|0777,UMSDOS_HLINK); + } + } + kfree (path); + } + } + } + }else{ + char *path = (char*)kmalloc(PATH_MAX,GFP_KERNEL); + if (path == NULL){ + ret = -ENOMEM; + }else{ + ret = umsdos_locate_path (oldinode,path); + if (ret == 0){ + dir->i_count++; + ret = umsdos_symlink_x (dir,name,len,path + ,S_IFREG|0777,UMSDOS_HLINK); + } + kfree (path); + } + } + } + umsdos_unlockcreate(olddir); + umsdos_unlockcreate(dir); + } + iput (olddir); + } + if (ret == 0){ + oldinode->i_nlink++; + ret = UMSDOS_notify_change (0,oldinode); + } + iput (oldinode); + iput (dir); + PRINTK (("umsdos_link %d\n",ret)); + return ret; +} +/* + Add a new file into the alternate directory. + The file is added to the real MSDOS directory. If successfull, it + is then added to the EDM file. + + Return the status of the operation. 0 mean success. +*/ +int UMSDOS_create ( + struct inode *dir, + const char *name, /* Name of the file to add */ + int len, /* Length of the name */ + int mode, /* Permission bit + file type ??? */ + struct inode **result) /* Will hold the inode of the newly created */ + /* file */ +{ + return umsdos_create_any (dir,name,len,mode,0,0,result); +} +/* + Add a sub-directory in a directory +*/ +int UMSDOS_mkdir( + struct inode * dir, + const char * name, + int len, + int mode) +{ + int ret = umsdos_nevercreat(dir,name,len,-EEXIST); + if (ret == 0){ + struct umsdos_info info; + ret = umsdos_parse (name,len,&info); + PRINTK (("umsdos_mkdir %d\n",ret)); + if (ret == 0){ + info.entry.mode = mode | S_IFDIR; + info.entry.rdev = 0; + info.entry.uid = current->euid; + info.entry.gid = (dir->i_mode & S_ISGID) + ? dir->i_gid : current->egid; + info.entry.ctime = info.entry.atime = info.entry.mtime + = CURRENT_TIME; + info.entry.flags = 0; + umsdos_lockcreate(dir); + info.entry.nlink = 1; + ret = umsdos_newentry (dir,&info); + PRINTK (("newentry %d ",ret)); + if (ret == 0){ + dir->i_count++; + ret = msdos_mkdir (dir,info.fake.fname,info.fake.len,mode); + if (ret != 0){ + umsdos_delentry (dir,&info,1); + /* #Specification: mkdir / Directory already exist in DOS + We do the same thing as for file creation. + For all user it is an error. + */ + }else{ + /* #Specification: mkdir / umsdos directory / create EMD + When we created a new sub-directory in a UMSDOS + directory (one with full UMSDOS semantic), we + create immediatly an EMD file in the new + sub-directory so it inherit UMSDOS semantic. + */ + struct inode *subdir; + ret = umsdos_real_lookup (dir,info.fake.fname + ,info.fake.len,&subdir); + if (ret == 0){ + struct inode *result; + ret = msdos_create (subdir,UMSDOS_EMD_FILE + ,UMSDOS_EMD_NAMELEN,S_IFREG|0777,&result); + subdir = NULL; + iput (result); + } + if (ret < 0){ + printk ("UMSDOS: Can't create empty --linux-.---\n"); + } + iput (subdir); + } + } + umsdos_unlockcreate(dir); + } + } + PRINTK (("umsdos_mkdir %d\n",ret)); + iput (dir); + return ret; +} +/* + Add a new device special file into a directory. +*/ +int UMSDOS_mknod( + struct inode * dir, + const char * name, + int len, + int mode, + int rdev) +{ + /* #Specification: Special files / strategy + Device special file, pipes, etc ... are created like normal + file in the msdos file system. Of course they remain empty. + + One strategy was to create thoses files only in the EMD file + since they were not important for MSDOS. The problem with + that, is that there were not getting inode number allocated. + The MSDOS filesystems is playing a nice game to fake inode + number, so why not use it. + + The absence of inode number compatible with those allocated + for ordinary files was causing major trouble with hard link + in particular and other parts of the kernel I guess. + */ + struct inode *inode; + int ret = umsdos_create_any (dir,name,len,mode,rdev,0,&inode); + iput (inode); + return ret; +} + +/* + Remove a sub-directory. +*/ +int UMSDOS_rmdir( + struct inode * dir, + const char * name, + int len) +{ + /* #Specification: style / iput strategy + In the UMSDOS project, I am trying to apply a single + programming style regarding inode management. Many + entry point are receiving an inode to act on, and must + do an iput() as soon as they are finished with + the inode. + + For simple case, there is no problem. When you introduce + error checking, you end up with many iput placed around the + code. + + The coding style I use all around is one where I am trying + to provide independant flow logic (I don't know how to + name this). With this style, code is easier to understand + but you rapidly get iput() all around. Here is an exemple + of what I am trying to avoid. + + if (a){ + ... + if(b){ + ... + } + ... + if (c){ + // Complexe state. Was b true ? + ... + } + ... + } + // Weird state + if (d){ + // ... + } + // Was iput finally done ? + return status; + + Here is the style I am using. Still sometime I do the + first when things are very simple (or very complicated :-( ) + + if (a){ + if (b){ + ... + }else if (c){ + // A single state gets here + } + }else if (d){ + ... + } + return status; + + Again, while this help clarifying the code, I often get a lot + of iput(), unlike the first style, where I can place few + "strategic" iput(). "strategic" also mean, more difficult + to place. + + So here is the style I will be using from now on in this project. + There is always an iput() at the end of a function (which has + to do an iput()). One iput by inode. There is also one iput() + at the places where a successful operation is achieved. This + iput() is often done by a sub-function (often from the msdos + file system). So I get one too many iput() ? At the place + where an iput() is done, the inode is simply nulled, disabling + the last one. + + if (a){ + if (b){ + ... + }else if (c){ + msdos_rmdir(dir,...); + dir = NULL; + } + }else if (d){ + ... + } + iput (dir); + return status; + + Note that the umsdos_lockcreate() and umsdos_unlockcreate() function + paire goes against this practice of "forgetting" the inode as soon + as possible. + */ + int ret = umsdos_nevercreat(dir,name,len,-EPERM); + if (ret == 0){ + struct inode *sdir; + dir->i_count++; + ret = UMSDOS_lookup (dir,name,len,&sdir); + PRINTK (("rmdir lookup %d ",ret)); + if (ret == 0){ + int empty; + umsdos_lockcreate(dir); + if ((empty = umsdos_isempty (sdir)) != 0){ + PRINTK (("isempty %d i_count %d ",empty,sdir->i_count)); + if (empty == 1){ + /* We have to removed the EMD file */ + ret = msdos_unlink(sdir,UMSDOS_EMD_FILE + ,UMSDOS_EMD_NAMELEN); + sdir = NULL; + } + /* sdir must be free before msdos_rmdir() */ + iput (sdir); + sdir = NULL; + PRINTK (("isempty ret %d nlink %d ",ret,dir->i_nlink)); + if (ret == 0){ + struct umsdos_info info; + dir->i_count++; + umsdos_parse (name,len,&info); + /* The findentry is there only to complete */ + /* the mangling */ + umsdos_findentry (dir,&info,2); + ret = msdos_rmdir (dir,info.fake.fname + ,info.fake.len); + if (ret == 0){ + ret = umsdos_delentry (dir,&info,1); + } + } + }else{ + /* + The subdirectory is not empty, so leave it there + */ + ret = -ENOTEMPTY; + } + iput(sdir); + umsdos_unlockcreate(dir); + } + } + iput (dir); + PRINTK (("umsdos_rmdir %d\n",ret)); + return ret; +} +/* + Remove a file from the directory. +*/ +int UMSDOS_unlink ( + struct inode * dir, + const char * name, + int len) +{ + struct umsdos_info info; + int ret = umsdos_nevercreat(dir,name,len,-EPERM); + if (ret == 0){ + ret = umsdos_parse (name,len,&info); + if (ret == 0){ + umsdos_lockcreate(dir); + ret = umsdos_findentry(dir,&info,1); + if (ret == 0){ + PRINTK (("UMSDOS_unlink %s ",info.fake.fname)); + if (info.entry.flags & UMSDOS_HLINK){ + /* #Specification: hard link / deleting a link + When we deletes a file, and this file is a link + we must substract 1 to the nlink field of the + hidden link. + + If the count goes to 0, we delete this hidden + link too. + */ + /* + First, get the inode of the hidden link + using the standard lookup function. + */ + struct inode *inode; + dir->i_count++; + ret = UMSDOS_lookup (dir,name,len,&inode); + if (ret == 0){ + PRINTK (("unlink nlink = %d ",inode->i_nlink)); + inode->i_nlink--; + if (inode->i_nlink == 0){ + struct inode *hdir = iget(inode->i_sb + ,inode->u.umsdos_i.i_dir_owner); + struct umsdos_dirent entry; + ret = umsdos_inode2entry (hdir,inode,&entry); + if (ret == 0){ + ret = UMSDOS_unlink (hdir,entry.name + ,entry.name_len); + }else{ + iput (hdir); + } + }else{ + ret = UMSDOS_notify_change (0,inode); + } + iput (inode); + } + } + if (ret == 0){ + ret = umsdos_delentry (dir,&info,0); + if (ret == 0){ + PRINTK (("Avant msdos_unlink %s ",info.fake.fname)); + dir->i_count++; + ret = msdos_unlink_umsdos (dir,info.fake.fname + ,info.fake.len); + PRINTK (("msdos_unlink %s %o ret %d ",info.fake.fname + ,info.entry.mode,ret)); + } + } + } + umsdos_unlockcreate(dir); + } + } + iput (dir); + PRINTK (("umsdos_unlink %d\n",ret)); + return ret; +} + +/* + Rename a file (move) in the file system. +*/ +int UMSDOS_rename( + struct inode * old_dir, + const char * old_name, + int old_len, + struct inode * new_dir, + const char * new_name, + int new_len) +{ + /* #Specification: weakness / rename + There is a case where UMSDOS rename has a different behavior + than normal UNIX file system. Renaming an open file across + directory boundary does not work. Renaming an open file within + a directory does work however. + + The problem (not sure) is in the linux VFS msdos driver. + I believe this is not a bug but a design feature, because + an inode number represent some sort of directory address + in the MSDOS directory structure. So moving the file into + another directory does not preserve the inode number. + */ + int ret = umsdos_nevercreat(new_dir,new_name,new_len,-EEXIST); + if (ret == 0){ + /* umsdos_rename_f eat the inode and we may need those later */ + old_dir->i_count++; + new_dir->i_count++; + ret = umsdos_rename_f (old_dir,old_name,old_len,new_dir,new_name + ,new_len,0); + if (ret == -EEXIST){ + /* #Specification: rename / new name exist + If the destination name already exist, it will + silently be removed. EXT2 does it this way + and this is the spec of SUNOS. So does UMSDOS. + + If the destination is an empty directory it will + also be removed. + */ + /* #Specification: rename / new name exist / possible flaw + The code to handle the deletion of the target (file + and directory) use to be in umsdos_rename_f, surrounded + by proper directory locking. This was insuring that only + one process could achieve a rename (modification) operation + in the source and destination directory. This was also + insuring the operation was "atomic". + + This has been changed because this was creating a kernel + stack overflow (stack is only 4k in the kernel). To avoid + the code doing the deletion of the target (if exist) has + been moved to a upper layer. umsdos_rename_f is tried + once and if it fails with EEXIST, the target is removed + and umsdos_rename_f is done again. + + This makes the code cleaner and (not sure) solve a + deadlock problem one tester was experiencing. + + The point is to mention that possibly, the semantic of + "rename" may be wrong. Anyone dare to check that :-) + Be aware that IF it is wrong, to produce the problem you + will need two process trying to rename a file to the + same target at the same time. Again, I am not sure it + is a problem at all. + */ + /* This is not super efficient but should work */ + new_dir->i_count++; + ret = UMSDOS_unlink (new_dir,new_name,new_len); +chkstk(); + PRINTK (("rename unlink ret %d %d -- ",ret,new_len)); + if (ret == -EISDIR){ + new_dir->i_count++; + ret = UMSDOS_rmdir (new_dir,new_name,new_len); +chkstk(); + PRINTK (("rename rmdir ret %d -- ",ret)); + } + if (ret == 0){ + ret = umsdos_rename_f (old_dir,old_name,old_len + ,new_dir,new_name,new_len,0); + new_dir = old_dir = NULL; + } + } + } + iput (new_dir); + iput (old_dir); + return ret; +} + diff -u --recursive --new-file v1.1.34/linux/fs/umsdos/notes linux/fs/umsdos/notes --- v1.1.34/linux/fs/umsdos/notes Thu Jan 1 02:00:00 1970 +++ linux/fs/umsdos/notes Sun Jul 24 17:39:45 1994 @@ -0,0 +1,17 @@ +This file contain idea and things I don't want to forget + +Possible bug in fs/read_write.c +Function sys_readdir() + + There is a call the verify_area that does not take in account + the count parameter. I guess it should read + + error = verify_area(VERIFY_WRITE, dirent, count*sizeof (*dirent)); + + instead of + + error = verify_area(VERIFY_WRITE, dirent, sizeof (*dirent)); + + Of course, now , count is always 1 + + diff -u --recursive --new-file v1.1.34/linux/fs/umsdos/rdir.c linux/fs/umsdos/rdir.c --- v1.1.34/linux/fs/umsdos/rdir.c Thu Jan 1 02:00:00 1970 +++ linux/fs/umsdos/rdir.c Sun Jul 24 17:39:46 1994 @@ -0,0 +1,239 @@ +/* + * linux/fs/umsdos/rdir.c + * + * Written 1994 by Jacques Gelinas + * + * Extended MS-DOS directory pure MS-DOS handling functions + * (For directory without EMD file). + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define PRINTK(x) +#define Printk(x) printk x + + +extern struct inode *pseudo_root; + +static int UMSDOS_rreaddir ( + struct inode *dir, + struct file *filp, + struct dirent *dirent, + int count) +{ + int ret = 0; + while (1){ + ret = msdos_readdir(dir,filp,dirent,count); + if (ret == 5 + && pseudo_root != NULL + && dir->i_sb->s_mounted == pseudo_root->i_sb->s_mounted){ + /* + In pseudo root mode, we must eliminate logically + the directory linux from the real root. + */ + char name[5]; + memcpy_fromfs (name,dirent->d_name,5); + if (memcmp(name,UMSDOS_PSDROOT_NAME,UMSDOS_PSDROOT_LEN)!=0) break; + }else{ + if (pseudo_root != NULL + && ret == 2 + && dir == dir->i_sb->s_mounted + && dir == pseudo_root->i_sb->s_mounted){ + char name[2]; + memcpy_fromfs (name,dirent->d_name,2); + if (name[0] == '.' && name[1] == '.'){ + put_fs_long (pseudo_root->i_ino,&dirent->d_ino); + } + } + break; + } + } + return ret; +} + +int UMSDOS_rlookup( + struct inode *dir, + const char *name, + int len, + struct inode **result) /* Will hold inode of the file, if successful */ +{ + int ret; + if (pseudo_root != NULL + && len == 2 + && name[0] == '.' + && name[1] == '.' + && dir == dir->i_sb->s_mounted + && dir == pseudo_root->i_sb->s_mounted){ + *result = pseudo_root; + pseudo_root->i_count++; + ret = 0; + /* #Specification: pseudo root / DOS/.. + In the real root directory (c:\), the directory .. + is the pseudo root (c:\linux). + */ + }else{ + ret = umsdos_real_lookup (dir,name,len,result); + if (ret == 0){ + struct inode *inode = *result; + if (inode == pseudo_root){ + /* #Specification: pseudo root / DOS/linux + Even in the real root directory (c:\), the directory + /linux won't show + */ + ret = -ENOENT; + iput (pseudo_root); + *result = NULL; + }else if (S_ISDIR(inode->i_mode)){ + /* We must place the proper function table */ + /* depending if this is a MsDOS directory or an UMSDOS directory */ + umsdos_setup_dir_inode(inode); + } + } + } + iput (dir); + return ret; +} + +static int UMSDOS_rrmdir ( + struct inode *dir, + const char *name, + int len) +{ + /* #Specification: dual mode / rmdir in a DOS directory + In a DOS (not EMD in it) directory, we use a reverse strategy + compared with an Umsdos directory. We assume that a subdirectory + of a DOS directory is also a DOS directory. This is not always + true (umssync may be used anywhere), but make sense. + + So we call msdos_rmdir() directly. If it failed with a -ENOTEMPTY + then we check if it is a Umsdos directory. We check if it is + really empty (only . .. and --linux-.--- in it). If it is true + we remove the EMD and do a msdos_rmdir() again. + + In a Umsdos directory, we assume all subdirectory are also + Umsdos directory, so we check the EMD file first. + */ + int ret; + if (umsdos_is_pseudodos(dir,name,len)){ + /* #Specification: pseudo root / rmdir /DOS + The pseudo sub-directory /DOS can't be removed! + This is done even if the pseudo root is not a Umsdos + directory anymore (very unlikely), but an accident (under + MsDOS) is always possible. + + EPERM is returned. + */ + ret = -EPERM; + }else{ + umsdos_lockcreate (dir); + dir->i_count++; + ret = msdos_rmdir (dir,name,len); + if (ret == -ENOTEMPTY){ + struct inode *sdir; + dir->i_count++; + ret = UMSDOS_rlookup (dir,name,len,&sdir); + PRINTK (("rrmdir lookup %d ",ret)); + if (ret == 0){ + int empty; + if ((empty = umsdos_isempty (sdir)) != 0){ + PRINTK (("isempty %d i_count %d ",empty,sdir->i_count)); + if (empty == 2){ + /* + Not a Umsdos directory, so the previous msdos_rmdir + was not lying :-) + */ + ret = -ENOTEMPTY; + }else if (empty == 1){ + /* We have to removed the EMD file */ + ret = msdos_unlink(sdir,UMSDOS_EMD_FILE + ,UMSDOS_EMD_NAMELEN); + sdir = NULL; + if (ret == 0){ + dir->i_count++; + ret = msdos_rmdir (dir,name,len); + } + } + }else{ + ret = -ENOTEMPTY; + } + iput (sdir); + } + } + umsdos_unlockcreate (dir); + } + iput (dir); + return ret; +} + +/* #Specification: dual mode / introduction + One goal of UMSDOS is to allow a practical and simple coexistence + between MsDOS and Linux in a single partition. Using the EMD file + in each directory, UMSDOS add Unix semantics and capabilities to + normal DOS file system. To help and simplify coexistence, here is + the logic related to the EMD file. + + If it is missing, then the directory is managed by the MsDOS driver. + The names are limited to DOS limits (8.3). No links, no device special + and pipe and so on. + + If it is there, it is the directory. If it is there but empty, then + the directory looks empty. The utility umssync allows synchronisation + of the real DOS directory and the EMD. + + Whenever umssync is applied to a directory without EMD, one is + created on the fly. The directory is promoted to full unix semantic. + Of course, the ls command will show exactly the same content as before + the umssync session. + + It is believed that the user/admin will promote directories to unix + semantic as needed. + + The strategy to implement this is to use two function table (struct + inode_operations). One for true UMSDOS directory and one for directory + with missing EMD. + + Functions related to the DOS semantic (but aware of UMSDOS) generally + have a "r" prefix (r for real) such as UMSDOS_rlookup, to differentiate + from the one with full UMSDOS semantic. +*/ +static struct file_operations umsdos_rdir_operations = { + NULL, /* lseek - default */ + UMSDOS_dir_read, /* read */ + NULL, /* write - bad */ + UMSDOS_rreaddir, /* readdir */ + NULL, /* select - default */ + UMSDOS_ioctl_dir, /* ioctl - default */ + NULL, /* mmap */ + NULL, /* no special open code */ + NULL, /* no special release code */ + NULL /* fsync */ +}; + +struct inode_operations umsdos_rdir_inode_operations = { + &umsdos_rdir_operations, /* default directory file-ops */ + msdos_create, /* create */ + UMSDOS_rlookup, /* lookup */ + NULL, /* link */ + msdos_unlink, /* unlink */ + NULL, /* symlink */ + msdos_mkdir, /* mkdir */ + UMSDOS_rrmdir, /* rmdir */ + NULL, /* mknod */ + msdos_rename, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL /* permission */ +}; + + diff -u --recursive --new-file v1.1.34/linux/fs/umsdos/symlink.c linux/fs/umsdos/symlink.c --- v1.1.34/linux/fs/umsdos/symlink.c Thu Jan 1 02:00:00 1970 +++ linux/fs/umsdos/symlink.c Sun Jul 24 17:39:46 1994 @@ -0,0 +1,144 @@ +/* + * linux/fs/umsdos/file.c + * + * Written 1992 by Jacques Gelinas + * inpired from linux/fs/msdos/file.c Werner Almesberger + * + * Extended MS-DOS regular file handling primitives + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define PRINTK(x) +#define Printk(x) printk x +/* + Read the data associate with the symlink. + Return lenght read in buffer or a negative error code. +*/ +static int umsdos_readlink_x ( + struct inode *inode, + char *buffer, + int (*msdos_read)(struct inode *, struct file *, char *, int), + int bufsiz) +{ + int ret = inode->i_size; + struct file filp; + filp.f_pos = 0; + if (ret > bufsiz) ret = bufsiz; + if ((*msdos_read) (inode, &filp, buffer,ret) != ret){ + ret = -EIO; + } + return ret; +} +/* + Follow a symbolic link chain by calling open_namei recursivly + until an inode is found. + + Return 0 if ok, or a negative error code if not. +*/ +static int UMSDOS_follow_link( + struct inode * dir, + struct inode * inode, + int flag, + int mode, + struct inode ** res_inode) +{ + int ret = -ELOOP; + *res_inode = NULL; + if (current->link_count < 5) { + char *path = (char*)kmalloc(PATH_MAX,GFP_KERNEL); + if (path == NULL){ + ret = -ENOMEM; + }else{ + if (!dir) { + dir = current->fs[1].root; + dir->i_count++; + } + if (!inode){ + PRINTK (("symlink: inode = NULL\n")); + ret = -ENOENT; + }else if (!S_ISLNK(inode->i_mode)){ + PRINTK (("symlink: Not ISLNK\n")); + *res_inode = inode; + inode = NULL; + ret = 0; + }else{ + ret = umsdos_readlink_x (inode,path + ,umsdos_file_read_kmem,PATH_MAX-1); + if (ret > 0){ + path[ret] = '\0'; + PRINTK (("follow :%s: %d ",path,ret)); + iput(inode); + inode = NULL; + current->link_count++; + ret = open_namei(path,flag,mode,res_inode,dir); + current->link_count--; + dir = NULL; + }else{ + ret = -EIO; + } + } + kfree (path); + } + } + iput(inode); + iput(dir); + PRINTK (("follow_link ret %d\n",ret)); + return ret; +} + +static int UMSDOS_readlink(struct inode * inode, char * buffer, int buflen) +{ + int ret = -EINVAL; + if (S_ISLNK(inode->i_mode)) { + ret = umsdos_readlink_x (inode,buffer,msdos_file_read,buflen); + } + PRINTK (("readlink %d %x bufsiz %d\n",ret,inode->i_mode,buflen)); + iput(inode); + return ret; + +} + +static struct file_operations umsdos_symlink_operations = { + NULL, /* lseek - default */ + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir - bad */ + NULL, /* select - default */ + NULL, /* ioctl - default */ + NULL, /* mmap */ + NULL, /* no special open is needed */ + NULL, /* release */ + NULL /* fsync */ +}; + +struct inode_operations umsdos_symlink_inode_operations = { + &umsdos_symlink_operations, /* default file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + UMSDOS_readlink, /* readlink */ + UMSDOS_follow_link, /* follow_link */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL /* permission */ +}; + + + diff -u --recursive --new-file v1.1.34/linux/include/linux/ext_fs.h linux/include/linux/ext_fs.h --- v1.1.34/linux/include/linux/ext_fs.h Wed Dec 1 14:44:15 1993 +++ linux/include/linux/ext_fs.h Sun Jul 24 17:39:46 1994 @@ -59,6 +59,7 @@ char name[EXT_NAME_LEN]; }; +#ifdef __KERNEL__ extern int ext_open(struct inode * inode, struct file * filp); extern void ext_release(struct inode * inode, struct file * filp); extern int ext_lookup(struct inode * dir,const char * name, int len, @@ -105,4 +106,5 @@ extern struct inode_operations ext_dir_inode_operations; extern struct inode_operations ext_symlink_inode_operations; +#endif /*__KERNEL__ */ #endif diff -u --recursive --new-file v1.1.34/linux/include/linux/fs.h linux/include/linux/fs.h --- v1.1.34/linux/include/linux/fs.h Sat Jul 23 17:38:37 1994 +++ linux/include/linux/fs.h Sun Jul 24 17:39:46 1994 @@ -158,11 +158,14 @@ #include #include #include +#include #include #include #include #include +#ifdef __KERNEL__ + struct inode { dev_t i_dev; unsigned long i_ino; @@ -203,6 +206,7 @@ struct ext2_inode_info ext2_i; struct hpfs_inode_info hpfs_i; struct msdos_inode_info msdos_i; + struct umsdos_inode_info umsdos_i; struct iso_inode_info isofs_i; struct nfs_inode_info nfs_i; struct xiafs_inode_info xiafs_i; @@ -313,6 +317,7 @@ int (*bmap) (struct inode *,int); void (*truncate) (struct inode *); int (*permission) (struct inode *, int); + int (*smap) (struct inode *,int); }; struct super_operations { @@ -332,8 +337,6 @@ int requires_dev; struct file_system_type * next; }; - -#ifdef __KERNEL__ extern int register_filesystem(struct file_system_type *); extern int unregister_filesystem(struct file_system_type *); diff -u --recursive --new-file v1.1.34/linux/include/linux/iso_fs.h linux/include/linux/iso_fs.h --- v1.1.34/linux/include/linux/iso_fs.h Wed Dec 1 14:44:15 1993 +++ linux/include/linux/iso_fs.h Sun Jul 24 17:39:46 1994 @@ -117,22 +117,6 @@ char name [0]; }; -extern int isonum_711(char *); -extern int isonum_712(char *); -extern int isonum_721(char *); -extern int isonum_722(char *); -extern int isonum_723(char *); -extern int isonum_731(char *); -extern int isonum_732(char *); -extern int isonum_733(char *); -extern int iso_date(char *, int); - -extern int parse_rock_ridge_inode(struct iso_directory_record *, struct inode *); -extern int get_rock_ridge_filename(struct iso_directory_record *, char ** name, int * len, struct inode *); - -extern char * get_rock_ridge_symlink(struct inode *); -extern int find_rock_ridge_relocation(struct iso_directory_record *, struct inode *); - #define ISOFS_BLOCK_BITS 11 #define ISOFS_BLOCK_SIZE 2048 @@ -160,7 +144,23 @@ #define ISOFS_FILE_BINARY 2 #define ISOFS_FILE_TEXT_M 3 +#ifdef __KERNEL__ +extern int isonum_711(char *); +extern int isonum_712(char *); +extern int isonum_721(char *); +extern int isonum_722(char *); +extern int isonum_723(char *); +extern int isonum_731(char *); +extern int isonum_732(char *); +extern int isonum_733(char *); +extern int iso_date(char *, int); + +extern int parse_rock_ridge_inode(struct iso_directory_record *, struct inode *); +extern int get_rock_ridge_filename(struct iso_directory_record *, char ** name, int * len, struct inode *); +extern char * get_rock_ridge_symlink(struct inode *); +extern int find_rock_ridge_relocation(struct iso_directory_record *, struct inode *); + /* The stuff that follows may be totally unneeded. I have not checked to see which prototypes we are still using. */ @@ -211,6 +211,8 @@ extern struct buffer_head * leak_check_bread(int dev, int block, int size); extern void leak_check_brelse(struct buffer_head * bh); #endif /* LEAK_CHECK */ + +#endif /* __KERNEL__ */ #endif diff -u --recursive --new-file v1.1.34/linux/include/linux/minix_fs.h linux/include/linux/minix_fs.h --- v1.1.34/linux/include/linux/minix_fs.h Mon Jan 3 10:40:41 1994 +++ linux/include/linux/minix_fs.h Sun Jul 24 17:39:46 1994 @@ -75,6 +75,8 @@ char name[0]; }; +#ifdef __KERNEL__ + extern int minix_lookup(struct inode * dir,const char * name, int len, struct inode ** result); extern int minix_create(struct inode * dir,const char * name, int len, int mode, @@ -115,5 +117,7 @@ extern struct inode_operations minix_file_inode_operations; extern struct inode_operations minix_dir_inode_operations; extern struct inode_operations minix_symlink_inode_operations; + +#endif /* __KERNEL__ */ #endif diff -u --recursive --new-file v1.1.34/linux/include/linux/mm.h linux/include/linux/mm.h --- v1.1.34/linux/include/linux/mm.h Sat Jul 23 17:38:38 1994 +++ linux/include/linux/mm.h Sun Jul 24 17:39:46 1994 @@ -92,6 +92,8 @@ }; /* end of planning stage */ +#ifdef __KERNEL__ + /* * Free area management */ @@ -221,21 +223,76 @@ #define GFP_NOBUFFER 0x04 -/* vm_ops not present page codes */ +/* + * vm_ops not present page codes for shared memory. + * + * Will go away eventually.. + */ #define SHM_SWP_TYPE 0x41 extern void shm_no_page (ulong *); -/* swap cache stuff (in swap.c) */ +/* + * swap cache stuff (in swap.c) + */ +#define SWAP_CACHE_INFO + extern unsigned long * swap_cache; +#ifdef SWAP_CACHE_INFO +extern unsigned long swap_cache_add_total; +extern unsigned long swap_cache_add_success; +extern unsigned long swap_cache_del_total; +extern unsigned long swap_cache_del_success; +extern unsigned long swap_cache_find_total; +extern unsigned long swap_cache_find_success; +#endif + extern inline unsigned long in_swap_cache(unsigned long addr) { return swap_cache[addr >> PAGE_SHIFT]; } -extern inline void swap_cache_invalidate(unsigned long addr) +extern inline long find_in_swap_cache (unsigned long addr) +{ + unsigned long entry; + +#ifdef SWAP_CACHE_INFO + swap_cache_find_total++; +#endif + __asm__ __volatile__("xchgl %0,%1" + :"=m" (swap_cache[addr >> PAGE_SHIFT]), + "=r" (entry) + :"0" (swap_cache[addr >> PAGE_SHIFT]), + "1" (0)); +#ifdef SWAP_CACHE_INFO + if (entry) + swap_cache_find_success++; +#endif + return entry; +} + +extern inline int delete_from_swap_cache(unsigned long addr) { - swap_cache[addr >> PAGE_SHIFT] = 0; + unsigned long entry; + +#ifdef SWAP_CACHE_INFO + swap_cache_del_total++; +#endif + __asm__ __volatile__("xchgl %0,%1" + :"=m" (swap_cache[addr >> PAGE_SHIFT]), + "=r" (entry) + :"0" (swap_cache[addr >> PAGE_SHIFT]), + "1" (0)); + if (entry) { +#ifdef SWAP_CACHE_INFO + swap_cache_del_success++; +#endif + swap_free(entry); + return 1; + } + return 0; } + +#endif /* __KERNEL__ */ #endif diff -u --recursive --new-file v1.1.34/linux/include/linux/msdos_fs.h linux/include/linux/msdos_fs.h --- v1.1.34/linux/include/linux/msdos_fs.h Wed Dec 1 14:44:15 1993 +++ linux/include/linux/msdos_fs.h Sun Jul 24 17:39:46 1994 @@ -4,7 +4,6 @@ /* * The MS-DOS filesystem constants/structures */ - #include #include #include @@ -109,6 +108,7 @@ #define MSDOS_MKATTR(m) ((m & S_IWUGO) ? ATTR_NONE : ATTR_RO) +#ifdef __KERNEL__ static inline struct buffer_head *msdos_sread(int dev,int sector,void **start) { @@ -161,6 +161,7 @@ extern int msdos_mkdir(struct inode *dir,const char *name,int len,int mode); extern int msdos_rmdir(struct inode *dir,const char *name,int len); extern int msdos_unlink(struct inode *dir,const char *name,int len); +extern int msdos_unlink_umsdos(struct inode *dir,const char *name,int len); extern int msdos_rename(struct inode *old_dir,const char *old_name,int old_len, struct inode *new_dir,const char *new_name,int new_len); @@ -179,12 +180,21 @@ /* dir.c */ extern struct inode_operations msdos_dir_inode_operations; - +extern int msdos_readdir (struct inode *inode, struct file *filp, + struct dirent *dirent, int count); /* file.c */ extern struct inode_operations msdos_file_inode_operations; +extern int msdos_file_read(struct inode *, struct file *, char *, int); +extern int msdos_file_write(struct inode *, struct file *, char *, int); extern struct inode_operations msdos_file_inode_operations_no_bmap; extern void msdos_truncate(struct inode *inode); + +/* mmap.c */ +extern int msdos_mmap (struct inode *, struct file *, unsigned long, size_t + ,int , unsigned long); + +#endif /* __KERNEL__ */ #endif diff -u --recursive --new-file v1.1.34/linux/include/linux/msdos_fs_i.h linux/include/linux/msdos_fs_i.h --- v1.1.34/linux/include/linux/msdos_fs_i.h Wed Dec 1 14:44:15 1993 +++ linux/include/linux/msdos_fs_i.h Sun Jul 24 17:39:46 1994 @@ -1,11 +1,33 @@ #ifndef _MSDOS_FS_I #define _MSDOS_FS_I +#ifndef _LINUX_CONFIG_H +#include +#endif +#ifndef _LINUX_PIPE_FS_I_H +#include +#endif + /* * MS-DOS file system inode data in memory */ struct msdos_inode_info { + /* + UMSDOS manage special file and fifo as normal empty + msdos file. fifo inode processing conflict with msdos + processing. So I insert the pipe_inode_info so the + information does not overlap. This increases the size of + the msdos_inode_info, but the clear winner here is + the ext2_inode_info. So it does not change anything to + the total size of a struct inode. + + I have not put it conditionnal. With the advent of loadable + file system drivers, it would be very easy to compile + a MsDOS FS driver unaware of UMSDOS and then later to + load a (then incompatible) UMSDOS FS driver. + */ + struct pipe_inode_info reserved; int i_start; /* first cluster or 0 */ int i_attrs; /* unused attribute bits */ int i_busy; /* file is either deleted but still open, or diff -u --recursive --new-file v1.1.34/linux/include/linux/mtio.h linux/include/linux/mtio.h --- v1.1.34/linux/include/linux/mtio.h Fri Jul 8 13:26:41 1994 +++ linux/include/linux/mtio.h Sat Jul 23 12:58:37 1994 @@ -1,4 +1,4 @@ -/* $Id: mtio.h,v 1.4 1992/11/18 01:32:03 root Exp root $ +/* $Id: mtio.h,v 1.13 1994/07/19 19:35:52 root Exp $ * * linux/mtio.h header file for Linux. Written by H. Bergman */ @@ -47,7 +47,7 @@ #define MTSETBLK 20 /* set block length (SCSI) */ #define MTSETDENSITY 21 /* set tape density (SCSI) */ #define MTSEEK 22 /* seek to block (Tandberg, etc.) */ -#define MTTELL 23 /* tell block (Tandber, etc.) */ +#define MTTELL 23 /* tell block (Tandberg, etc.) */ #define MTSETDRVBUFFER 24 /* set the drive buffering according to SCSI-2 */ /* ordinary buffered operation with code 1 */ @@ -70,8 +70,11 @@ daddr_t mt_blkno; /* current block number */ }; + + /* - * Constants for mt_type. Not all of these are supported. + * Constants for mt_type. Not all of these are supported, + * and these are not all of the ones that are supported. */ #define MT_ISUNKNOWN 0x01 #define MT_ISQIC02 0x02 /* Generic QIC-02 tape streamer */ @@ -82,8 +85,10 @@ #define MT_ISARCHIVE_VP60I 0x07 /* Archive VP60i, QIC-02 */ #define MT_ISARCHIVE_2150L 0x08 /* Archive Viper 2150L */ #define MT_ISARCHIVE_2060L 0x09 /* Archive Viper 2060L */ +#define MT_ISARCHIVESC499 0x0A /* Archive SC-499 QIC-36 controller */ #define MT_ISQIC02_ALL_FEATURES 0x0F /* Generic QIC-02 with all features */ #define MT_ISWT5099EEN24 0x11 /* Wangtek 5099-een24, 60MB, QIC-24 */ +#define MT_ISTEAC_MT2ST 0x12 /* Teac MT-2ST 155mb drive, Teac DC-1 card (Wangtek type) */ #define MT_ISEVEREX_FT40A 0x32 /* Everex FT40A (QIC-40) */ #define MT_ISDDS1 0x51 /* DDS device without partitions */ #define MT_ISDDS2 0x52 /* DDS device with partitions */ @@ -93,20 +98,20 @@ /* QIC-40/QIC-80 ftape supported drives. * 20bit vendor ID + 0x800000 */ -#define MT_ISFTAPE_UNKNOWN 0x800000 -#define MT_ISCMSDJ10_DJ20 0x800047 -#define MT_ISCMSDJ10_DJ20_NEW 0x8011c4 -#define MT_ISARCHIVE_5580I 0x800005 -#define MT_ISARCHIVE_XL9250I 0x80014a -#define MT_ISARCHIVE_31250Q 0x800146 -#define MT_ISINSIGHT_80 0x810005 -#define MT_ISCONNER_C250MQT 0x80014c -#define MT_ISWANGTEK_2040F 0x8001c1 -#define MT_ISWANGTEK_2080F 0x8001c8 -#define MT_ISIOMEGA_250 0x808880 -#define MT_ISSUMMIT_SE150 0x800180 -#define MT_ISSUMMIT_SE250 0x800181 -#define MT_ISESCOM_IDTBU120E 0x800140 +#define MT_ISFTAPE_UNKNOWN 0x800000 +#define MT_ISCMSDJ10_DJ20 0x800047 +#define MT_ISCMSDJ10_DJ20_NEW 0x8011c4 +#define MT_ISARCHIVE_5580I 0x800005 +#define MT_ISARCHIVE_XL9250I 0x80014a +#define MT_ISARCHIVE_31250Q 0x800146 +#define MT_ISINSIGHT_80 0x810005 +#define MT_ISCONNER_C250MQT 0x80014c +#define MT_ISWANGTEK_2040F 0x8001c1 +#define MT_ISWANGTEK_2080F 0x8001c8 +#define MT_ISIOMEGA_250 0x808880 +#define MT_ISSUMMIT_SE150 0x800180 +#define MT_ISSUMMIT_SE250 0x800181 +#define MT_ISESCOM_IDTBU120E 0x800140 struct mt_tape_info { long t_type; /* device type id (mt_type) */ @@ -123,24 +128,27 @@ {MT_ISARCHIVE_VP60I, "Archive VP60i, QIC-02"}, \ {MT_ISARCHIVE_2150L, "Archive Viper 2150L"}, \ {MT_ISARCHIVE_2060L, "Archive Viper 2060L"}, \ + {MT_ISARCHIVESC499, "Archive SC-499 QIC-36 controller"}, \ + {MT_ISQIC02_ALL_FEATURES, "Generic QIC-02 tape, all features"}, \ {MT_ISWT5099EEN24, "Wangtek 5099-een24, 60MB"}, \ + {MT_ISTEAC_MT2ST, "Teac MT-2ST 155mb data cassette drive"}, \ {MT_ISEVEREX_FT40A, "Everex FT40A, QIC-40"}, \ {MT_ISSCSI1, "Generic SCSI-1 tape"}, \ {MT_ISSCSI2, "Generic SCSI-2 tape"}, \ {MT_ISFTAPE_UNKNOWN, "Unknown floppy interface tape drive"},\ {MT_ISCMSDJ10_DJ20, "Colorado DJ-10/DJ-20"},\ - {MT_ISCMSDJ10_DJ20_NEW, "Colorado DJ-10/DJ-20 (new)"},\ + {MT_ISCMSDJ10_DJ20_NEW, "Colorado DJ-10/DJ-20 (new)"},\ {MT_ISARCHIVE_5580I, "Archive 5580i"},\ - {MT_ISARCHIVE_XL9250I, "Archive XL9250i [Conner/Escom]"},\ + {MT_ISARCHIVE_XL9250I, "Archive XL9250i [Conner/Escom]"},\ {MT_ISARCHIVE_31250Q, "Escom/Archive 31250Q"},\ - {MT_ISINSIGHT_80, "Insight 80 Mb"},\ - {MT_ISCONNER_C250MQT, "Conner C250MQT"},\ - {MT_ISWANGTEK_2040F, "Wangtek 3040F"},\ - {MT_ISWANGTEK_2080F, "Wangtek 3080F"},\ - {MT_ISIOMEGA_250, "Iomega 250"},\ - {MT_ISSUMMIT_SE150, "Summit SE 150"},\ - {MT_ISSUMMIT_SE250, "Summit SE 250/Mountain FS8000"},\ - {MT_ISESCOM_IDTBU120E, "Identity IDTBU120E, Escom?"},\ + {MT_ISINSIGHT_80, "Insight 80 Mb"},\ + {MT_ISCONNER_C250MQT, "Conner C250MQT"},\ + {MT_ISWANGTEK_2040F, "Wangtek 3040F"},\ + {MT_ISWANGTEK_2080F, "Wangtek 3080F"},\ + {MT_ISIOMEGA_250, "Iomega 250"},\ + {MT_ISSUMMIT_SE150, "Summit SE 150"},\ + {MT_ISSUMMIT_SE250, "Summit SE 250/Mountain FS8000"},\ + {MT_ISESCOM_IDTBU120E, "Identity IDTBU120E, Escom?"},\ {0, NULL} \ } @@ -157,6 +165,12 @@ #define MTIOCGET _IOR('m', 2, struct mtget) /* get tape status */ #define MTIOCPOS _IOR('m', 3, struct mtpos) /* get tape position */ +/* The next two are used by the QIC-02 driver for runtime reconfiguration. + * See tpqic02.h for struct mtconfiginfo. + */ +#define MTIOCGETCONFIG _IOR('m', 4, struct mtconfiginfo) /* get tape config */ +#define MTIOCSETCONFIG _IOW('m', 5, struct mtconfiginfo) /* set tape config */ + /* Generic Mag Tape (device independent) status macros for examining * mt_gstat -- HP-UX compatible. @@ -206,4 +220,3 @@ #define MT_ST_TWO_FM 0x10 #endif /* _LINUX_MTIO_H */ - diff -u --recursive --new-file v1.1.34/linux/include/linux/net.h linux/include/linux/net.h --- v1.1.34/linux/include/linux/net.h Tue May 24 00:34:56 1994 +++ linux/include/linux/net.h Sun Jul 24 17:39:46 1994 @@ -54,7 +54,7 @@ #define SO_ACCEPTCON (1<<16) /* performed a listen */ - +#ifdef __KERNEL__ /* * Internal representation of a socket. not all the fields are used by * all configurations: @@ -130,5 +130,5 @@ extern int sock_awaitconn(struct socket *mysock, struct socket *servsock); extern int sock_register(int family, struct proto_ops *ops); - +#endif /* __KERNEL__ */ #endif /* _LINUX_NET_H */ diff -u --recursive --new-file v1.1.34/linux/include/linux/nfs_fs.h linux/include/linux/nfs_fs.h --- v1.1.34/linux/include/linux/nfs_fs.h Thu Jul 14 00:48:26 1994 +++ linux/include/linux/nfs_fs.h Sun Jul 24 17:39:46 1994 @@ -45,6 +45,8 @@ #define NFS_SERVER(inode) (&(inode)->i_sb->u.nfs_sb.s_server) #define NFS_FH(inode) (&(inode)->u.nfs_i.fhandle) +#ifdef __KERNEL__ + /* linux/fs/nfs/proc.c */ extern int nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, @@ -112,5 +114,7 @@ extern int nfs_mmap(struct inode * inode, struct file * file, unsigned long addr, size_t len, int prot, unsigned long off); + +#endif /* __KERNEL__ */ #endif diff -u --recursive --new-file v1.1.34/linux/include/linux/sched.h linux/include/linux/sched.h --- v1.1.34/linux/include/linux/sched.h Mon Jul 18 14:48:30 1994 +++ linux/include/linux/sched.h Sun Jul 24 17:39:46 1994 @@ -352,6 +352,8 @@ /* mm */ { INIT_MM } \ } +#ifdef __KERNEL__ + extern struct task_struct init_task; extern struct task_struct *task[NR_TASKS]; extern struct task_struct *last_task_used_math; @@ -619,5 +621,7 @@ : /* no output */ \ :"m" (current->debugreg[register]) \ :"dx"); + +#endif /* __KERNEL__ */ #endif diff -u --recursive --new-file v1.1.34/linux/include/linux/timer.h linux/include/linux/timer.h --- v1.1.34/linux/include/linux/timer.h Sat Jul 9 16:30:47 1994 +++ linux/include/linux/timer.h Sat Jul 23 12:58:37 1994 @@ -27,7 +27,7 @@ * * COPRO_TIMER 387 timeout for buggy hardware.. * - * TAPE_QIC02_TIMER timer for QIC-02 tape driver (it's not hardcoded) + * QIC02_TAPE_TIMER timer for QIC-02 tape driver (it's not hardcoded) * * MCD_TIMER Mitsumi CD-ROM Timer */ @@ -43,7 +43,7 @@ #define SOUND_TIMER 20 #define COPRO_TIMER 21 -#define TAPE_QIC02_TIMER 22 /* hhb */ +#define QIC02_TAPE_TIMER 22 /* hhb */ #define MCD_TIMER 23 #define HD_TIMER2 24 diff -u --recursive --new-file v1.1.34/linux/include/linux/tpqic02.h linux/include/linux/tpqic02.h --- v1.1.34/linux/include/linux/tpqic02.h Wed Dec 1 14:44:15 1993 +++ linux/include/linux/tpqic02.h Sat Jul 23 12:58:38 1994 @@ -1,10 +1,10 @@ -/* $Id: tpqic02.h,v 0.16 1993/04/19 23:15:39 root Exp root $ +/* $Id: tpqic02.h,v 0.25 1994/07/21 02:16:30 root Exp root $ * * Include file for QIC-02 driver for Linux. * - * Copyright (c) 1992 by H. H. Bergman. All rights reserved. + * Copyright (c) 1992, 1993, 1994 by H. H. Bergman. All rights reserved. * - * ******* USER CONFIG SECTION BELOW ******* + * ******* USER CONFIG SECTION BELOW (Near line 70) ******* */ #ifndef _LINUX_TPQIC02_H @@ -12,15 +12,25 @@ #include -#if CONFIG_TAPE_QIC02 +#if CONFIG_QIC02_TAPE -/* need to have TAPE_QIC02_DRIVE and TAPE_QIC02_IFC expand to something */ +/* need to have QIC02_TAPE_DRIVE and QIC02_TAPE_IFC expand to something */ #include -/* make TAPE_QIC02_IFC expand to something */ +/* Make QIC02_TAPE_IFC expand to something. + * + * The only difference between WANGTEK and EVEREX is in the + * handling of the DMA channel 3. + * Note that the driver maps EVEREX to WANGTEK internally for speed + * reasons. Externally WANGTEK==1, EVEREX==2, ARCHIVE==3. + * These must correspond to the values used in qic02config(1). + * + * Support for Mountain controllers was added by Erik Jacobson + * and severely hacked by me. -- hhb + */ #define WANGTEK 1 /* don't know about Wangtek QIC-36 */ -#define EVEREX WANGTEK /* I heard *some* of these are identical */ +#define EVEREX (WANGTEK+1) /* I heard *some* of these are identical */ #define EVEREX_811V EVEREX /* With TEAC MT 2ST 45D */ #define EVEREX_831V EVEREX #define ARCHIVE 3 @@ -28,86 +38,153 @@ #define ARCHIVE_SC402 ARCHIVE /* don't know much about SC400 */ #define ARCHIVE_SC499 ARCHIVE /* SC402 and SC499R should be identical */ +#define MOUNTAIN 5 /* Mountain Computer Interface */ + /*********** START OF USER CONFIGURABLE SECTION ************/ -/* Tape configuration: +/* Tape configuration: Select DRIVE, IFC, PORT, IRQ and DMA below. + * Runtime (re)configuration is not supported yet. * - * Tape drive configuration: (MT_IS* constants are defined in sys/mtio.h) + * Tape drive configuration: (MT_IS* constants are defined in mtio.h) * - * TAPE_QIC02_DRIVE = MT_ISWT5150 + * QIC02_TAPE_DRIVE = MT_ISWT5150 * - Wangtek 5150, format: up to QIC-150. - * TAPE_QIC02_DRIVE = MT_ISQIC02_ALL_FEATURES - * - Enables some optional QIC commands that some drives may lack. + * QIC02_TAPE_DRIVE = MT_ISQIC02_ALL_FEATURES + * - Enables some optional QIC02 commands that some drives may lack. * It is provided so you can check which are supported by your drive. * Refer to tpqic02.h for others. * - * Supported interface cards: TAPE_QIC02_IFC = + * Supported interface cards: QIC02_TAPE_IFC = * WANGTEK, * ARCHIVE_SC402, ARCHIVE_SC499. (both same programming interface) * * Make sure you have the I/O ports/DMA channels * and IRQ stuff configured properly! - * NOTE: Check for conflicts with TAPE_QIC02_TIMER in timer.h. + * NOTE: There may be other device drivers using the same major + * number. This must be avoided. Check for timer.h conflicts too. + * + * If you have an EVEREX EV-831 card and you are using DMA channel 3, + * you will probably have to ``#define QIC02_TAPE_DMA3_FIX'' below. */ -#define TAPE_QIC02_DRIVE MT_ISQIC02_ALL_FEATURES /* drive type */ -/* #define TAPE_QIC02_DRIVE MT_ISWT5150 */ -#define TAPE_QIC02_IFC WANGTEK /* interface card type */ -/* #define TAPE_QIC02_IFC ARCHIVE */ -#define TAPE_QIC02_PORT 0x300 /* controller port adress */ -#define TAPE_QIC02_IRQ 5 /* Muhammad, please don't use 2 here. -- Hennus */ -#define TAPE_QIC02_DMA 1 /* either 1 or 3, because 2 is used by the floppy */ +/* CONFIG_QIC02_DYNCONF can be defined in autoconf.h, by `make config' */ + +/*** #undef CONFIG_QIC02_DYNCONF ***/ +#ifndef CONFIG_QIC02_DYNCONF +#define QIC02_TAPE_DRIVE MT_ISQIC02_ALL_FEATURES /* drive type */ +/* #define QIC02_TAPE_DRIVE MT_ISWT5150 */ +/* #define QIC02_TAPE_DRIVE MT_ISARCHIVE_5945L2 */ +/* #define QIC02_TAPE_DRIVE MT_ISTEAC_MT2ST */ +/* #define QIC02_TAPE_DRIVE MT_ISARCHIVE_2150L */ +/* #define QIC02_TAPE_DRIVE MT_ISARCHIVESC499 */ + +/* Either WANGTEK, ARCHIVE or MOUNTAIN. Not EVEREX. + * If you have an EVEREX, use WANGTEK and try the DMA3_FIX below. + */ +#define QIC02_TAPE_IFC WANGTEK /* interface card type */ +/* #define QIC02_TAPE_IFC ARCHIVE */ +/* #define QIC02_TAPE_IFC MOUNTAIN */ + +#define QIC02_TAPE_PORT 0x300 /* controller port adress */ +#define QIC02_TAPE_IRQ 5 /* For IRQ2, use 9 here, others normal. */ +#define QIC02_TAPE_DMA 1 /* either 1 or 3, because 2 is used by the floppy */ + +/* If DMA3 doesn't work, but DMA1 does, and you have a + * Wangtek/Everex card, you can try #define-ing the flag + * below. Note that you should also change the DACK jumper + * for Wangtek/Everex cards when changing the DMA channel. + */ +#undef QIC02_TAPE_DMA3_FIX + /************ END OF USER CONFIGURABLE SECTION *************/ +/* I put the stuff above in config.in, but a few recompiles, to + * verify different configurations, and several days later I decided + * to change it back again. + */ + -/* NOTE: TP_HAVE_DENS should distinguish between available densities + +/* NOTE: TP_HAVE_DENS should distinguish between available densities (?) * NOTE: Drive select is not implemented -- I have only one tape streamer, * so I'm unable and unmotivated to test and implement that. ;-) ;-) */ -#if TAPE_QIC02_DRIVE == MT_ISWT5150 -#define TP_HAVE_DENS -#define TP_HAVE_BSF /* nope */ -#define TP_HAVE_FSR /* nope */ -#define TP_HAVE_BSR /* nope */ -#define TP_HAVE_EOD /* most of the time */ -#define TP_HAVE_RAS1 -#define TP_HAVE_RAS2 - -#elif TAPE_QIC02_DRIVE == MT_ISARCHIVESC499 /* Archive SC-499 QIC-36 controller */ -#define TP_HAVE_DENS /* can do set density (QIC-11 / QIC-24) */ -#define TP_HAVE_FSR /* can skip one block forwards */ -#define TP_HAVE_BSR /* can skip one block backwards */ -#define TP_HAVE_EOD /* can seek to end of recorded data */ -#define TP_HAVE_RAS1 /* can run selftest 1 */ -#define TP_HAVE_RAS2 /* can run selftest 2 */ +#if QIC02_TAPE_DRIVE == MT_ISWT5150 +#define TP_HAVE_DENS 1 +#define TP_HAVE_BSF 0 /* nope */ +#define TP_HAVE_FSR 0 /* nope */ +#define TP_HAVE_BSR 0 /* nope */ +#define TP_HAVE_EOD 0 /* most of the time */ +#define TP_HAVE_SEEK 0 +#define TP_HAVE_TELL 0 +#define TP_HAVE_RAS1 1 +#define TP_HAVE_RAS2 1 + +#elif QIC02_TAPE_DRIVE == MT_ISARCHIVESC499 /* Archive SC-499 QIC-36 controller */ +#define TP_HAVE_DENS 1 /* can do set density (QIC-11 / QIC-24) */ +#define TP_HAVE_BSF 0 +#define TP_HAVE_FSR 1 /* can skip one block forwards */ +#define TP_HAVE_BSR 1 /* can skip one block backwards */ +#define TP_HAVE_EOD 1 /* can seek to end of recorded data */ +#define TP_HAVE_SEEK 0 +#define TP_HAVE_TELL 0 +#define TP_HAVE_RAS1 1 /* can run selftest 1 */ +#define TP_HAVE_RAS2 1 /* can run selftest 2 */ +/* These last two selftests shouldn't be used yet! */ + +#elif (QIC02_TAPE_DRIVE == MT_ISARCHIVE_2060L) || (QIC02_TAPE_DRIVE == MT_ISARCHIVE_2150L) +#define TP_HAVE_DENS 1 /* can do set density (QIC-24 / QIC-120 / QIC-150) */ +#define TP_HAVE_BSF 0 +#define TP_HAVE_FSR 1 /* can skip one block forwards */ +#define TP_HAVE_BSR 1 /* can skip one block backwards */ +#define TP_HAVE_EOD 1 /* can seek to end of recorded data */ +#define TP_HAVE_TELL 1 /* can read current block address */ +#define TP_HAVE_SEEK 1 /* can seek to block */ +#define TP_HAVE_RAS1 1 /* can run selftest 1 */ +#define TP_HAVE_RAS2 1 /* can run selftest 2 */ /* These last two selftests shouldn't be used yet! */ -#elif (TAPE_QIC02_DRIVE == MT_ISARCHIVE_2060L) || (TAPE_QIC02_DRIVE == MT_ISARCHIVE_2150L) -#define TP_HAVE_DENS /* can do set density (QIC-24 / QIC-120 / QIC-150) */ -#define TP_HAVE_FSR /* can skip one block forwards */ -#define TP_HAVE_BSR /* can skip one block backwards */ -#define TP_HAVE_EOD /* can seek to end of recorded data */ -#define TP_HAVE_TELL /* can read current block address */ -#define TP_HAVE_SEEK /* can seek to block */ -#define TP_HAVE_RAS1 /* can run selftest 1 */ -#define TP_HAVE_RAS2 /* can run selftest 2 */ +#elif QIC02_TAPE_DRIVE == MT_ISARCHIVE_5945L2 +/* can anyone verify this entry?? */ +#define TP_HAVE_DENS 1 /* can do set density?? (QIC-24??) */ +#define TP_HAVE_BSF 0 +#define TP_HAVE_FSR 1 /* can skip one block forwards */ +#define TP_HAVE_BSR 1 /* can skip one block backwards */ +#define TP_HAVE_EOD 1 /* can seek to end of recorded data */ +#define TP_HAVE_TELL 1 /* can read current block address */ +#define TP_HAVE_SEEK 1 /* can seek to block */ +#define TP_HAVE_RAS1 1 /* can run selftest 1 */ +#define TP_HAVE_RAS2 1 /* can run selftest 2 */ /* These last two selftests shouldn't be used yet! */ -#elif TAPE_QIC02_DRIVE == MT_ISQIC02_ALL_FEATURES -#define TP_HAVE_DENS /* can do set density */ -#define TP_HAVE_BSF /* can search filemark backwards */ -#define TP_HAVE_FSR /* can skip one block forwards */ -#define TP_HAVE_BSR /* can skip one block backwards */ -#define TP_HAVE_EOD /* can seek to end of recorded data */ -#define TP_HAVE_SEEK /* seek to block address */ -#define TP_HAVE_TELL /* tell current block address */ -#define TP_HAVE_RAS1 /* can run selftest 1 */ -#define TP_HAVE_RAS2 /* can run selftest 2 */ +#elif QIC02_TAPE_DRIVE == MT_ISTEAC_MT2ST +/* can anyone verify this entry?? */ +#define TP_HAVE_DENS 0 /* cannot do set density?? (QIC-150?) */ +#define TP_HAVE_BSF 0 +#define TP_HAVE_FSR 1 /* can skip one block forwards */ +#define TP_HAVE_BSR 1 /* can skip one block backwards */ +#define TP_HAVE_EOD 1 /* can seek to end of recorded data */ +#define TP_HAVE_SEEK 1 /* can seek to block */ +#define TP_HAVE_TELL 1 /* can read current block address */ +#define TP_HAVE_RAS1 1 /* can run selftest 1 */ +#define TP_HAVE_RAS2 1 /* can run selftest 2 */ /* These last two selftests shouldn't be used yet! */ +#elif QIC02_TAPE_DRIVE == MT_ISQIC02_ALL_FEATURES +#define TP_HAVE_DENS 1 /* can do set density */ +#define TP_HAVE_BSF 1 /* can search filemark backwards */ +#define TP_HAVE_FSR 1 /* can skip one block forwards */ +#define TP_HAVE_BSR 1 /* can skip one block backwards */ +#define TP_HAVE_EOD 1 /* can seek to end of recorded data */ +#define TP_HAVE_SEEK 1 /* seek to block address */ +#define TP_HAVE_TELL 1 /* tell current block address */ +#define TP_HAVE_RAS1 1 /* can run selftest 1 */ +#define TP_HAVE_RAS2 1 /* can run selftest 2 */ +/* These last two selftests shouldn't be used yet! */ + #else #error No QIC-02 tape drive type defined! @@ -118,91 +195,245 @@ */ #endif - -/* NR_BLK_BUF is a `tuneable parameter'. If you're really low on - * kernel space, you could decrease it to 1, or if you got a very - * slow machine, you could increase it up to 128 blocks. Less kernel - * buffer blocks result in more context-switching. - */ -#define NR_BLK_BUF 20 /* max 128 blocks */ -#define TAPE_BLKSIZE 512 /* streamer tape block size (fixed) */ -#define TPQBUF_SIZE (TAPE_BLKSIZE*NR_BLK_BUF) /* buffer size */ - +#endif /* !CONFIG_QIC02_DYNCONF */ -#define BLOCKS_BEYOND_EW 2 /* nr of blocks after Early Warning hole */ -#if TAPE_QIC02_IFC == WANGTEK - /* Wangtek interface card port locations */ -# define QIC_STAT_PORT TAPE_QIC02_PORT -# define QIC_CTL_PORT TAPE_QIC02_PORT -# define QIC_CMD_PORT (TAPE_QIC02_PORT+1) -# define QIC_DATA_PORT (TAPE_QIC02_PORT+1) +/* WANGTEK interface card specifics */ +#define WT_QIC02_STAT_PORT (QIC02_TAPE_PORT) +#define WT_QIC02_CTL_PORT (QIC02_TAPE_PORT) +#define WT_QIC02_CMD_PORT (QIC02_TAPE_PORT+1) +#define WT_QIC02_DATA_PORT (QIC02_TAPE_PORT+1) /* status register bits (Active LOW!) */ -# define QIC_STAT_READY 0x01 -# define QIC_STAT_EXCEPTION 0x02 -# define QIC_STAT_MASK (QIC_STAT_READY|QIC_STAT_EXCEPTION) - -# define QIC_STAT_RESETMASK 0x07 -# define QIC_STAT_RESETVAL (QIC_STAT_RESETMASK & ~QIC_STAT_EXCEPTION) - -/* controller register (QIC_CTL_PORT) bits */ -# define WT_CTL_ONLINE 0x01 -# define QIC_CTL_RESET 0x02 -# define QIC_CTL_REQUEST 0x04 -# define WT_CTL_CMDOFF 0xC0 -# if TAPE_QIC02_DMA == 3 /* dip-switches alone don't seem to cut it */ -# define WT_CTL_DMA 0x10 /* enable dma chan3 */ -# elif TAPE_QIC02_DMA == 1 -# define WT_CTL_DMA 0x08 /* enable dma chan1 or chan2 */ -# else -# error Unsupported or incorrect DMA configuration. -# endif +#define WT_QIC02_STAT_READY 0x01 +#define WT_QIC02_STAT_EXCEPTION 0x02 +#define WT_QIC02_STAT_MASK (WT_QIC02_STAT_READY|WT_QIC02_STAT_EXCEPTION) + +#define WT_QIC02_STAT_RESETMASK 0x07 +#define WT_QIC02_STAT_RESETVAL (WT_QIC02_STAT_RESETMASK & ~WT_QIC02_STAT_EXCEPTION) + +/* controller register (QIC02_CTL_PORT) bits */ +#define WT_QIC02_CTL_RESET 0x02 +#define WT_QIC02_CTL_REQUEST 0x04 +#define WT_CTL_ONLINE 0x01 +#define WT_CTL_CMDOFF 0xC0 + +#define WT_CTL_DMA3 0x10 /* enable dma chan3 */ +#define WT_CTL_DMA1 0x08 /* enable dma chan1 or chan2 */ -#elif TAPE_QIC02_IFC == ARCHIVE - /* Archive interface card port locations */ -# define QIC_STAT_PORT (TAPE_QIC02_PORT+1) -# define QIC_CTL_PORT (TAPE_QIC02_PORT+1) -# define QIC_CMD_PORT (TAPE_QIC02_PORT) -# define QIC_DATA_PORT (TAPE_QIC02_PORT) -# define AR_START_DMA_PORT (TAPE_QIC02_PORT+2) -# define AR_RESET_DMA_PORT (TAPE_QIC02_PORT+3) - - /* STAT port bits */ -# define AR_STAT_IRQF 0x80 /* active high, interrupt request flag */ -# define QIC_STAT_READY 0x40 /* active low */ -# define QIC_STAT_EXCEPTION 0x20 /* active low */ -# define QIC_STAT_MASK (QIC_STAT_READY|QIC_STAT_EXCEPTION) -# define AR_STAT_DMADONE 0x10 /* active high, DMA done */ -# define AR_STAT_DIRC 0x08 /* active high, direction */ -# define QIC_STAT_RESETMASK 0x70 /* check RDY,EXC,DMADONE */ -# define QIC_STAT_RESETVAL ((QIC_STAT_RESETMASK & ~AR_STAT_IRQF & ~QIC_STAT_EXCEPTION) | AR_STAT_DMADONE) - /* CTL port bits */ -# define QIC_CTL_RESET 0x80 /* drive reset */ -# define QIC_CTL_REQUEST 0x40 /* notify of new command */ -# define AR_CTL_IEN 0x20 /* interrupt enable */ -# define AR_CTL_DNIEN 0x10 /* done-interrupt enable */ + +/* ARCHIVE interface card specifics */ +#define AR_QIC02_STAT_PORT (QIC02_TAPE_PORT+1) +#define AR_QIC02_CTL_PORT (QIC02_TAPE_PORT+1) +#define AR_QIC02_CMD_PORT (QIC02_TAPE_PORT) +#define AR_QIC02_DATA_PORT (QIC02_TAPE_PORT) + +#define AR_START_DMA_PORT (QIC02_TAPE_PORT+2) +#define AR_RESET_DMA_PORT (QIC02_TAPE_PORT+3) + +/* STAT port bits */ +#define AR_STAT_IRQF 0x80 /* active high, interrupt request flag */ +#define AR_QIC02_STAT_READY 0x40 /* active low */ +#define AR_QIC02_STAT_EXCEPTION 0x20 /* active low */ +#define AR_QIC02_STAT_MASK (AR_QIC02_STAT_READY|AR_QIC02_STAT_EXCEPTION) +#define AR_STAT_DMADONE 0x10 /* active high, DMA done */ +#define AR_STAT_DIRC 0x08 /* active high, direction */ + +#define AR_QIC02_STAT_RESETMASK 0x70 /* check RDY,EXC,DMADONE */ +#define AR_QIC02_STAT_RESETVAL ((AR_QIC02_STAT_RESETMASK & ~AR_STAT_IRQF & ~AR_QIC02_STAT_EXCEPTION) | AR_STAT_DMADONE) + +/* CTL port bits */ +#define AR_QIC02_CTL_RESET 0x80 /* drive reset */ +#define AR_QIC02_CTL_REQUEST 0x40 /* notify of new command */ +#define AR_CTL_IEN 0x20 /* interrupt enable */ +#define AR_CTL_DNIEN 0x10 /* done-interrupt enable */ /* Note: All of these bits are cleared automatically when writing to * AR_RESET_DMA_PORT. So AR_CTL_IEN and AR_CTL_DNIEN must be * reprogrammed before the write to AR_START_DMA_PORT. */ -# if TAPE_QIC02_DMA > 3 /* channel 2 is used by the floppy driver */ -# error DMA channels other than 1 and 3 are not supported. + +/* MOUNTAIN interface specifics */ +#define MTN_QIC02_STAT_PORT (QIC02_TAPE_PORT+1) +#define MTN_QIC02_CTL_PORT (QIC02_TAPE_PORT+1) +#define MTN_QIC02_CMD_PORT (QIC02_TAPE_PORT) +#define MTN_QIC02_DATA_PORT (QIC02_TAPE_PORT) + +#define MTN_W_SELECT_DMA_PORT (QIC02_TAPE_PORT+2) +#define MTN_R_DESELECT_DMA_PORT (QIC02_TAPE_PORT+2) +#define MTN_W_DMA_WRITE_PORT (QIC02_TAPE_PORT+3) + +/* STAT port bits */ +#define MTN_QIC02_STAT_READY 0x02 /* active low */ +#define MTN_QIC02_STAT_EXCEPTION 0x04 /* active low */ +#define MTN_QIC02_STAT_MASK (MTN_QIC02_STAT_READY|MTN_QIC02_STAT_EXCEPTION) +#define MTN_STAT_DMADONE 0x01 /* active high, DMA done */ + +#define MTN_QIC02_STAT_RESETMASK 0x07 /* check RDY,EXC,DMADONE */ +#define MTN_QIC02_STAT_RESETVAL ((MTN_QIC02_STAT_RESETMASK & ~MTN_QIC02_STAT_EXCEPTION) | MTN_STAT_DMADONE) + + /* CTL port bits */ +#define MTN_QIC02_CTL_RESET_NOT 0x80 /* drive reset, active low */ +#define MTN_QIC02_CTL_RESET 0x80 /* Fodder #definition to keep gcc happy */ + +#define MTN_QIC02_CTL_ONLINE 0x40 /* Put drive on line */ +#define MTN_QIC02_CTL_REQUEST 0x20 /* notify of new command */ +#define MTN_QIC02_CTL_IRQ_DRIVER 0x10 /* Enable IRQ tristate driver */ +#define MTN_QIC02_CTL_DMA_DRIVER 0x08 /* Enable DMA tristate driver */ +#define MTN_CTL_EXC_IEN 0x04 /* Exception interrupt enable */ +#define MTN_CTL_RDY_IEN 0x02 /* Ready interrupt enable */ +#define MTN_CTL_DNIEN 0x01 /* done-interrupt enable */ + +#define MTN_CTL_ONLINE (MTN_QIC02_CTL_RESET_NOT | MTN_QIC02_CTL_IRQ_DRIVER | MTN_QIC02_CTL_DMA_DRIVER) + + +#ifndef CONFIG_QIC02_DYNCONF + +# define QIC02_TAPE_DEBUG (qic02_tape_debug) + +# if QIC02_TAPE_IFC == WANGTEK +# define QIC02_STAT_PORT WT_QIC02_STAT_PORT +# define QIC02_CTL_PORT WT_QIC02_CTL_PORT +# define QIC02_CMD_PORT WT_QIC02_CMD_PORT +# define QIC02_DATA_PORT WT_QIC02_DATA_PORT + +# define QIC02_STAT_READY WT_QIC02_STAT_READY +# define QIC02_STAT_EXCEPTION WT_QIC02_STAT_EXCEPTION +# define QIC02_STAT_MASK WT_QIC02_STAT_MASK +# define QIC02_STAT_RESETMASK WT_QIC02_STAT_RESETMASK +# define QIC02_STAT_RESETVAL WT_QIC02_STAT_RESETVAL + +# define QIC02_CTL_RESET WT_QIC02_CTL_RESET +# define QIC02_CTL_REQUEST WT_QIC02_CTL_REQUEST + +# if QIC02_TAPE_DMA == 3 +# ifdef QIC02_TAPE_DMA3_FIX +# define WT_CTL_DMA WT_CTL_DMA1 +# else +# define WT_CTL_DMA WT_CTL_DMA3 +# endif +# elif QIC02_TAPE_DMA == 1 +# define WT_CTL_DMA WT_CTL_DMA1 +# else +# error Unsupported or incorrect DMA configuration. +# endif + +# elif QIC02_TAPE_IFC == ARCHIVE +# define QIC02_STAT_PORT AR_QIC02_STAT_PORT +# define QIC02_CTL_PORT AR_QIC02_CTL_PORT +# define QIC02_CMD_PORT AR_QIC02_CMD_PORT +# define QIC02_DATA_PORT AR_QIC02_DATA_PORT + +# define QIC02_STAT_READY AR_QIC02_STAT_READY +# define QIC02_STAT_EXCEPTION AR_QIC02_STAT_EXCEPTION +# define QIC02_STAT_MASK AR_QIC02_STAT_MASK +# define QIC02_STAT_RESETMASK AR_QIC02_STAT_RESETMASK +# define QIC02_STAT_RESETVAL AR_QIC02_STAT_RESETVAL + +# define QIC02_CTL_RESET AR_QIC02_CTL_RESET +# define QIC02_CTL_REQUEST AR_QIC02_CTL_REQUEST + +# if QIC02_TAPE_DMA > 3 /* channel 2 is used by the floppy driver */ +# error DMA channels other than 1 and 3 are not supported. +# endif + +# elif QIC02_TAPE_IFC == MOUNTAIN +# define QIC02_STAT_PORT MTN_QIC02_STAT_PORT +# define QIC02_CTL_PORT MTN_QIC02_CTL_PORT +# define QIC02_CMD_PORT MTN_QIC02_CMD_PORT +# define QIC02_DATA_PORT MTN_QIC02_DATA_PORT + +# define QIC02_STAT_READY MTN_QIC02_STAT_READY +# define QIC02_STAT_EXCEPTION MTN_QIC02_STAT_EXCEPTION +# define QIC02_STAT_MASK MTN_QIC02_STAT_MASK +# define QIC02_STAT_RESETMASK MTN_QIC02_STAT_RESETMASK +# define QIC02_STAT_RESETVAL MTN_QIC02_STAT_RESETVAL + +# define QIC02_CTL_RESET MTN_QIC02_CTL_RESET +# define QIC02_CTL_REQUEST MTN_QIC02_CTL_REQUEST + +# if QIC02_TAPE_DMA > 3 /* channel 2 is used by the floppy driver */ +# error DMA channels other than 1 and 3 are not supported. +# endif + +# else +# error No valid interface card specified! +# endif /* QIC02_TAPE_IFC */ + + + /* An ugly hack to make sure WT_CTL_DMA is defined even for the + * static, non-Wangtek case. The alternative was even worse. + */ +# ifndef WT_CTL_DMA +# define WT_CTL_DMA WT_CTL_DMA1 # endif -#else -# error No valid interface card specified! -#endif /* TAPE_QIC02_IFC */ +/*******************/ + +#else /* !CONFIG_QIC02_DYNCONF */ + +/* Now the runtime config version, using variables instead of constants. + * + * qic02_tape_dynconf is R/O for the kernel, set from userspace. + * qic02_tape_ccb is private to the driver, R/W. + */ + +# define QIC02_TAPE_DRIVE (qic02_tape_dynconf.mt_type) +# define QIC02_TAPE_IFC (qic02_tape_ccb.ifc_type) +# define QIC02_TAPE_IRQ (qic02_tape_dynconf.irqnr) +# define QIC02_TAPE_DMA (qic02_tape_dynconf.dmanr) +# define QIC02_TAPE_PORT (qic02_tape_dynconf.port) +# define WT_CTL_DMA (qic02_tape_ccb.dma_enable_value) +# define QIC02_TAPE_DEBUG (qic02_tape_dynconf.debug) + +# define QIC02_STAT_PORT (qic02_tape_ccb.port_stat) +# define QIC02_CTL_PORT (qic02_tape_ccb.port_ctl) +# define QIC02_CMD_PORT (qic02_tape_ccb.port_cmd) +# define QIC02_DATA_PORT (qic02_tape_ccb.port_data) + +# define QIC02_STAT_READY (qic02_tape_ccb.stat_ready) +# define QIC02_STAT_EXCEPTION (qic02_tape_ccb.stat_exception) +# define QIC02_STAT_MASK (qic02_tape_ccb.stat_mask) + +# define QIC02_STAT_RESETMASK (qic02_tape_ccb.stat_resetmask) +# define QIC02_STAT_RESETVAL (qic02_tape_ccb.stat_resetval) + +# define QIC02_CTL_RESET (qic02_tape_ccb.ctl_reset) +# define QIC02_CTL_REQUEST (qic02_tape_ccb.ctl_request) + +# define TP_HAVE_DENS (qic02_tape_dynconf.have_dens) +# define TP_HAVE_BSF (qic02_tape_dynconf.have_bsf) +# define TP_HAVE_FSR (qic02_tape_dynconf.have_fsr) +# define TP_HAVE_BSR (qic02_tape_dynconf.have_bsr) +# define TP_HAVE_EOD (qic02_tape_dynconf.have_eod) +# define TP_HAVE_SEEK (qic02_tape_dynconf.have_seek) +# define TP_HAVE_TELL (qic02_tape_dynconf.have_tell) +# define TP_HAVE_RAS1 (qic02_tape_dynconf.have_ras1) +# define TP_HAVE_RAS2 (qic02_tape_dynconf.have_ras2) + +#endif /* CONFIG_QIC02_DYNCONF */ + + +/* "Vendor Unique" codes */ +/* Archive seek & tell stuff */ +#define AR_QCMDV_TELL_BLK 0xAE /* read current block address */ +#define AR_QCMDV_SEEK_BLK 0xAD /* seek to specific block */ +#define AR_SEEK_BUF_SIZE 3 /* address is 3 bytes */ + + +/* + * Misc common stuff + */ + /* Standard QIC-02 commands -- rev F. All QIC-02 drives must support these */ #define QCMD_SEL_1 0x01 /* select drive 1 */ #define QCMD_SEL_2 0x02 /* select drive 2 */ #define QCMD_SEL_3 0x04 /* select drive 3 */ #define QCMD_SEL_4 0x08 /* select drive 4 */ -#define QCMD_REWIND 0x21 /* rewind tape*/ +#define QCMD_REWIND 0x21 /* rewind tape */ #define QCMD_ERASE 0x22 /* erase tape */ #define QCMD_RETEN 0x24 /* retension tape */ #define QCMD_WRT_DATA 0x40 /* write data */ @@ -234,14 +465,6 @@ -/* "Vendor Unique" codes */ -#if defined(MT_ISARCHIVESC499) || defined(MT_ISARCHIVE_2150L) -# define QCMDV_TELL_BLK 0xAE /* read current block address */ -# define QCMDV_SEEK_BLK 0xAD /* seek to specific block */ -# define SEEK_BUF_SIZE 3 /* address is 3 bytes */ -#endif - - /* Optional, QFA (Quick File Access) commands. * Not all drives support this, but those that do could use these commands * to implement semi-non-sequential access. `mt fsf` would benefit from this. @@ -249,7 +472,7 @@ * causing some incompatibility problems wrt std QIC-02 data exchange. * It would be useful to cache the directory info, but that might be tricky * to do in kernel-space. [Size constraints.] - * Refer to QIC-02, appendix A for more information. + * Refer to the QIC-02 specs, appendix A for more information. * I have no idea how other *nix variants implement QFA. * I have no idea which drives support QFA and which don't. */ @@ -262,6 +485,30 @@ + +/* + * Debugging flags + */ +#define TPQD_SENSE_TEXT 0x0001 +#define TPQD_SENSE_CNTS 0x0002 +#define TPQD_REWIND 0x0004 +#define TPQD_TERM_CYCLE 0x0008 +#define TPQD_IOCTLS 0x0010 +#define TPQD_DMAX 0x0020 +#define TPQD_BLKSZ 0x0040 +#define TPQD_MISC 0x0080 + +#define TPQD_DEBUG 0x0100 + +#define TPQD_DIAGS 0x1000 + +#define TPQD_ALWAYS 0x8000 + +#define TPQD_DEFAULT_FLAGS 0x01fc + +#define TPQDBG(f) ((QIC02_TAPE_DEBUG) & (TPQD_##f)) + + /* Minor device codes for tapes: * |7|6|5|4|3|2|1|0| * | \ | / \ | / |_____ 1=rewind on close, 0=no rewind on close @@ -275,8 +522,9 @@ /* rewind is only done if data has been transfered */ #define TP_DENS(dev) ((MINOR(dev) >> 1) & 0x07) /* tape density */ #define TP_UNIT(dev) ((MINOR(dev) >> 4) & 0x07) /* unit number */ -#define TP_DIAGS(dev) (MINOR(dev) & 0x80) /* print excessive diagnostics */ +/* print excessive diagnostics */ +#define TP_DIAGS(dev) (QIC02_TAPE_DEBUG & TPQD_DIAGS) /* status codes returned by a WTS_RDSTAT call */ struct tpstatus { /* sizeof(short)==2), LSB first */ @@ -309,9 +557,10 @@ #define REPORT_ERR1 (TP_ILL|TP_NDT|TP_MBD|TP_PAR) +/* exception numbers */ #define EXC_UNKNOWN 0 /* (extra) Unknown exception code */ -#define EXC_NCART 1 /* No cartridge */ -#define EXC_NDRV 2 /* No drive */ +#define EXC_NDRV 1 /* No drive */ +#define EXC_NCART 2 /* No cartridge */ #define EXC_WP 3 /* Write protected */ #define EXC_EOM 4 /* EOM */ #define EXC_RWA 5 /* read/write abort */ @@ -345,10 +594,10 @@ #define TIM_R (8*60*HZ) /* 8 minutes (retensioning) */ #define TIM_F (2*3600*HZ) /* est. 1.2hr for full tape read/write+2 retens */ -#define TIMERON(t) timer_table[TAPE_QIC02_TIMER].expires = jiffies + (t); \ - timer_active |= (1< #include #include @@ -13,6 +14,7 @@ #include + /* * Note: don't mess with NR_PTYS until you understand the tty minor * number allocation game... @@ -322,4 +324,5 @@ extern int vt_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg); +#endif /* __KERNEL__ */ #endif diff -u --recursive --new-file v1.1.34/linux/include/linux/umsdos_fs.h linux/include/linux/umsdos_fs.h --- v1.1.34/linux/include/linux/umsdos_fs.h Thu Jan 1 02:00:00 1970 +++ linux/include/linux/umsdos_fs.h Sun Jul 24 22:27:55 1994 @@ -0,0 +1,108 @@ +#ifndef LINUX_UMSDOS_FS_H +#define LINUX_UMSDOS_FS_H + +#define UMSDOS_VERSION 0 +#define UMSDOS_RELEASE 3 + +#ifndef LINUX_FS_H +#include +#endif + +/* This is the file acting as a directory extension */ +#define UMSDOS_EMD_FILE "--linux-.---" +#define UMSDOS_EMD_NAMELEN 12 +#define UMSDOS_PSDROOT_NAME "linux" +#define UMSDOS_PSDROOT_LEN 5 + +struct umsdos_fake_info { + char fname[13]; + int len; +}; + +#define UMSDOS_MAXNAME 220 +/* This structure is 256 bytes large, depending on the name, only part */ +/* of it is written to disk */ +struct umsdos_dirent { + unsigned char name_len; /* if == 0, then this entry is not used */ + unsigned char flags; /* UMSDOS_xxxx */ + unsigned short nlink; /* How many hard links point to this entry */ + uid_t uid; /* Owner user id */ + gid_t gid; /* Group id */ + time_t atime; /* Access time */ + time_t mtime; /* Last modification time */ + time_t ctime; /* Creation time */ + dev_t rdev; /* major and minor number of a device */ + /* special file */ + umode_t mode; /* Standard UNIX permissions bits + type of */ + char spare[12]; /* unused bytes for future extensions */ + /* file, see linux/stat.h */ + char name[UMSDOS_MAXNAME]; /* Not '\0' terminated */ + /* but '\0' padded, so it will allow */ + /* for adding news fields in this record */ + /* by reducing the size of name[] */ +}; +#define UMSDOS_HIDDEN 1 /* Never show this entry in directory search */ +#define UMSDOS_HLINK 2 /* It is a (pseudo) hard link */ + +/* #Specification: EMD file / record size + Entry are 64 bytes wide in the EMD file. It allows for a 30 characters + name. If a name is longer, contiguous entries are allocated. So a + umsdos_dirent may span multiple records. +*/ +#define UMSDOS_REC_SIZE 64 + +/* Translation between MSDOS name and UMSDOS name */ +struct umsdos_info{ + int msdos_reject; /* Tell if the file name is invalid for MSDOS */ + /* See umsdos_parse */ + struct umsdos_fake_info fake; + struct umsdos_dirent entry; + off_t f_pos; /* offset of the entry in the EMD file */ + /* or offset where the entry may be store */ + /* if it is a new entry */ + int recsize; /* Record size needed to store entry */ +}; + +/* Definitions for ioctl (number randomly chosen) */ +/* The next ioctl commands operate only on the DOS directory */ +/* The file umsdos_progs/umsdosio.c contain a string table */ +/* based on the order of those definition. Keep it in sync */ +#define UMSDOS_READDIR_DOS 1234 /* Do a readdir of the DOS directory */ +#define UMSDOS_UNLINK_DOS 1235 /* Erase in the DOS directory only */ +#define UMSDOS_RMDIR_DOS 1236 /* rmdir in the DOS directory only */ +#define UMSDOS_STAT_DOS 1237 /* Get info about a file */ +/* The next ioctl commands operate only on the EMD file */ +#define UMSDOS_CREAT_EMD 1238 /* Create a file */ +#define UMSDOS_UNLINK_EMD 1239 /* unlink (rmdir) a file */ +#define UMSDOS_READDIR_EMD 1240 /* read the EMD file only. */ +#define UMSDOS_GETVERSION 1241 /* Get the release number of UMSDOS */ +#define UMSDOS_INIT_EMD 1242 /* Create the EMD file if not there */ +#define UMSDOS_DOS_SETUP 1243 /* Set the defaults of the MsDOS driver */ + +#ifndef _SYS_STAT_H +#include +#endif + +struct umsdos_ioctl{ + struct dirent dos_dirent; + struct umsdos_dirent umsdos_dirent; + struct stat stat; + char version,release; +}; + +/* Different macros to access struct umsdos_dirent */ +#define EDM_ENTRY_ISUSED(e) ((e)->name_len!=0) + +#ifdef __KERNEL__ + +extern struct inode_operations umsdos_dir_inode_operations; +extern struct file_operations umsdos_file_operations; +extern struct inode_operations umsdos_file_inode_operations; +extern struct inode_operations umsdos_file_inode_operations_no_bmap; +extern struct inode_operations umsdos_symlink_inode_operations; + +#include + +#endif /* __KERNEL__ */ + +#endif diff -u --recursive --new-file v1.1.34/linux/include/linux/umsdos_fs.p linux/include/linux/umsdos_fs.p --- v1.1.34/linux/include/linux/umsdos_fs.p Thu Jan 1 02:00:00 1970 +++ linux/include/linux/umsdos_fs.p Sun Jul 24 17:39:47 1994 @@ -0,0 +1,133 @@ +/* check.c 20/07/94 10.08.36 */ +void check_page_tables (void); +/* dir.c 22/07/94 01.06.58 */ +int UMSDOS_dir_read (struct inode *inode, + struct file *filp, + char *buf, + int count); +void umsdos_lookup_patch (struct inode *dir, + struct inode *inode, + struct umsdos_dirent *entry, + off_t emd_pos); +int umsdos_inode2entry (struct inode *dir, + struct inode *inode, + struct umsdos_dirent *entry); +int umsdos_locate_path (struct inode *inode, char *path); +int umsdos_is_pseudodos (struct inode *dir, const char *name, int len); +int UMSDOS_lookup (struct inode *dir, + const char *name, + int len, + struct inode **result); +int umsdos_hlink2inode (struct inode *hlink, struct inode **result); +/* emd.c 22/07/94 01.06.38 */ +int umsdos_readdir_kmem (struct inode *inode, + struct file *filp, + struct dirent *dirent, + int count); +int umsdos_file_read_kmem (struct inode *inode, + struct file *filp, + char *buf, + int count); +int umsdos_file_write_kmem (struct inode *inode, + struct file *filp, + char *buf, + int count); +int umsdos_emd_dir_write (struct inode *emd_dir, + struct file *filp, + char *buf, + int count); +int umsdos_emd_dir_read (struct inode *emd_dir, + struct file *filp, + char *buf, + int count); +struct inode *umsdos_emd_dir_lookup (struct inode *dir, int creat); +int umsdos_emd_dir_readentry (struct inode *emd_dir, + struct file *filp, + struct umsdos_dirent *entry); +int umsdos_writeentry (struct inode *dir, + struct inode *emd_dir, + struct umsdos_info *info, + int free_entry); +int umsdos_newentry (struct inode *dir, struct umsdos_info *info); +int umsdos_newhidden (struct inode *dir, struct umsdos_info *info); +int umsdos_delentry (struct inode *dir, + struct umsdos_info *info, + int isdir); +int umsdos_isempty (struct inode *dir); +int umsdos_findentry (struct inode *dir, + struct umsdos_info *info, + int expect); +/* file.c 20/07/94 10.08.36 */ +/* inode.c 20/07/94 10.08.36 */ +void UMSDOS_put_inode (struct inode *inode); +void UMSDOS_put_super (struct super_block *sb); +void UMSDOS_statfs (struct super_block *sb, struct statfs *buf); +int umsdos_real_lookup (struct inode *dir, + const char *name, + int len, + struct inode **result); +void umsdos_setup_dir_inode (struct inode *inode); +void umsdos_set_dirinfo (struct inode *inode, + struct inode *dir, + off_t f_pos); +int umsdos_isinit (struct inode *inode); +void umsdos_patch_inode (struct inode *inode, + struct inode *dir, + off_t f_pos); +int umsdos_get_dirowner (struct inode *inode, struct inode **result); +void UMSDOS_read_inode (struct inode *inode); +void UMSDOS_write_inode (struct inode *inode); +int UMSDOS_notify_change (int flags, struct inode *inode); +struct super_block *UMSDOS_read_super (struct super_block *s, + void *data, + int silent); +/* ioctl.c 20/07/94 10.08.36 */ +int UMSDOS_ioctl_dir (struct inode *dir, + struct file *filp, + unsigned int cmd, + unsigned long data); +/* mangle.c 20/07/94 10.08.36 */ +void umsdos_manglename (struct umsdos_info *info); +int umsdos_evalrecsize (int len); +int umsdos_parse (const char *fname, int len, struct umsdos_info *info); +/* namei.c 22/07/94 00.59.28 */ +void umsdos_lockcreate (struct inode *dir); +void umsdos_startlookup (struct inode *dir); +void umsdos_unlockcreate (struct inode *dir); +void umsdos_endlookup (struct inode *dir); +int UMSDOS_symlink (struct inode *dir, + const char *name, + int len, + const char *symname); +int UMSDOS_link (struct inode *oldinode, + struct inode *dir, + const char *name, + int len); +int UMSDOS_create (struct inode *dir, + const char *name, + int len, + int mode, + struct inode **result); +int UMSDOS_mkdir (struct inode *dir, + const char *name, + int len, + int mode); +int UMSDOS_mknod (struct inode *dir, + const char *name, + int len, + int mode, + int rdev); +int UMSDOS_rmdir (struct inode *dir, const char *name, int len); +int UMSDOS_unlink (struct inode *dir, const char *name, int len); +int UMSDOS_rename (struct inode *old_dir, + const char *old_name, + int old_len, + struct inode *new_dir, + const char *new_name, + int new_len); +/* rdir.c 20/07/94 10.08.38 */ +int UMSDOS_rlookup (struct inode *dir, + const char *name, + int len, + struct inode **result); +/* symlink.c 22/07/94 00.59.10 */ diff -u --recursive --new-file v1.1.34/linux/include/linux/umsdos_fs_i.h linux/include/linux/umsdos_fs_i.h --- v1.1.34/linux/include/linux/umsdos_fs_i.h Thu Jan 1 02:00:00 1970 +++ linux/include/linux/umsdos_fs_i.h Sun Jul 24 17:39:47 1994 @@ -0,0 +1,140 @@ +#ifndef UMSDOS_FS_I_H +#define UMSDOS_FS_I_H + +#ifndef _LINUX_TYPES_H +#include +#endif +#include +#include + +/* #Specification: strategy / in memory inode + Here is the information specific to the inode of the UMSDOS file + system. This information is added to the end of the standard struct + inode. Each file system has its own extension to struct inode, + so do the umsdos file system. + + The strategy is to have the umsdos_inode_info as a superset of + the msdos_inode_info, since most of the time the job is done + by the msdos fs code. + + So we duplicate the msdos_inode_info, and add our own info at the + end. + + For all file type (and directory) the inode has a reference to: + the directory which hold this entry: i_dir_owner + The EMD file of i_dir_owner: i_emd_owner + The offset in this EMD file of the entry: pos + + For directory, we also have a reference to the inode of its + own EMD file. Also, we have dir_locking_info to help synchronise + file creation and file lookup. This data is sharing space with + the pipe_inode_info not used by directory. See also msdos_fs_i.h + for more information about pipe_inode_info and msdos_inode_info. + + Special file and fifo do have an inode which correspond to an + empty MSDOS file. + + symlink are processed mostly like regular file. The content is the + link. + + fifos add there own extension to the inode. I have reserved some + space for fifos side by side with msdos_inode_info. This is just + to for the show, because msdos_inode_info already include the + pipe_inode_info. + + The UMSDOS specific extension is placed after the union. +*/ +struct dir_locking_info { + struct wait_queue *p; + short int looking; /* How many process doing a lookup */ + short int creating; /* Is there any creation going on here */ + /* Only one at a time, although one */ + /* may recursivly lock, so it is a counter */ + long pid; /* pid of the process owning the creation */ + /* lock */ +}; +struct umsdos_inode_info { + union { + struct msdos_inode_info msdos_info; + struct pipe_inode_info pipe_info; + struct dir_locking_info dir_info; + }u; /* Simply a filler, never referenced by fs/umsdos/... */ + unsigned long i_dir_owner; /* Inode of the dir which hold this */ + /* entry */ + unsigned long i_emd_owner; /* Inode of the EMD file of i_dir_owner */ + off_t pos; /* Entry offset in the emd_owner file */ + /* The rest is used only if this inode describe a directory */ + unsigned long i_emd_dir; /* Inode of the EMD file of this inode */ +}; + +#endif +#ifndef UMSDOS_FS_I_H +#define UMSDOS_FS_I_H + +#ifndef _LINUX_TYPES_H +#include +#endif +#include +#include + +/* #Specification: strategy / in memory inode + Here is the information specific to the inode of the UMSDOS file + system. This information is added to the end of the standard struct + inode. Each file system has its own extension to struct inode, + so do the umsdos file system. + + The strategy is to have the umsdos_inode_info as a superset of + the msdos_inode_info, since most of the time the job is done + by the msdos fs code. + + So we duplicate the msdos_inode_info, and add our own info at the + end. + + For all file type (and directory) the inode has a reference to: + the directory which hold this entry: i_dir_owner + The EMD file of i_dir_owner: i_emd_owner + The offset in this EMD file of the entry: pos + + For directory, we also have a reference to the inode of its + own EMD file. Also, we have dir_locking_info to help synchronise + file creation and file lookup. This data is sharing space with + the pipe_inode_info not used by directory. See also msdos_fs_i.h + for more information about pipe_inode_info and msdos_inode_info. + + Special file and fifo do have an inode which correspond to an + empty MSDOS file. + + symlink are processed mostly like regular file. The content is the + link. + + fifos add there own extension to the inode. I have reserved some + space for fifos side by side with msdos_inode_info. This is just + to for the show, because msdos_inode_info already include the + pipe_inode_info. + + The UMSDOS specific extension is placed after the union. +*/ +struct dir_locking_info { + struct wait_queue *p; + short int looking; /* How many process doing a lookup */ + short int creating; /* Is there any creation going on here */ + /* Only one at a time, although one */ + /* may recursivly lock, so it is a counter */ + long pid; /* pid of the process owning the creation */ + /* lock */ +}; +struct umsdos_inode_info { + union { + struct msdos_inode_info msdos_info; + struct pipe_inode_info pipe_info; + struct dir_locking_info dir_info; + }u; /* Simply a filler, never referenced by fs/umsdos/... */ + unsigned long i_dir_owner; /* Inode of the dir which hold this */ + /* entry */ + unsigned long i_emd_owner; /* Inode of the EMD file of i_dir_owner */ + off_t pos; /* Entry offset in the emd_owner file */ + /* The rest is used only if this inode describe a directory */ + unsigned long i_emd_dir; /* Inode of the EMD file of this inode */ +}; + +#endif diff -u --recursive --new-file v1.1.34/linux/init/main.c linux/init/main.c --- v1.1.34/linux/init/main.c Tue Jun 28 22:30:27 1994 +++ linux/init/main.c Sun Jul 24 17:39:47 1994 @@ -500,6 +500,22 @@ setup(); sprintf(term, "TERM=con%dx%d", ORIG_VIDEO_COLS, ORIG_VIDEO_LINES); + + #ifdef CONFIG_UMSDOS_FS + { + /* + When mounting a umsdos fs as root, we detect + the pseudo_root (/linux) and initialise it here. + pseudo_root is defined in fs/umsdos/inode.c + */ + extern struct inode *pseudo_root; + if (pseudo_root != NULL){ + current->fs->root = pseudo_root; + current->fs->pwd = pseudo_root; + } + } + #endif + (void) open("/dev/tty1",O_RDWR,0); (void) dup(0); (void) dup(0); diff -u --recursive --new-file v1.1.34/linux/kernel/dma.c linux/kernel/dma.c --- v1.1.34/linux/kernel/dma.c Wed Dec 1 14:44:15 1993 +++ linux/kernel/dma.c Sat Jul 23 12:58:38 1994 @@ -26,11 +26,11 @@ /* Channel n is busy iff dma_chan_busy[n] != 0. - * DMA0 is reserved for DRAM refresh, I think. - * DMA4 is reserved for cascading (?). + * DMA0 used to be reserved for DRAM refresh, but apparently not any more... + * DMA4 is reserved for cascading. */ static volatile unsigned int dma_chan_busy[MAX_DMA_CHANNELS] = { - 1, 0, 0, 0, 1, 0, 0, 0 + 0, 0, 0, 0, 1, 0, 0, 0 }; diff -u --recursive --new-file v1.1.34/linux/mm/memory.c linux/mm/memory.c --- v1.1.34/linux/mm/memory.c Sat Jul 23 17:38:38 1994 +++ linux/mm/memory.c Sun Jul 24 17:39:47 1994 @@ -269,10 +269,8 @@ } if (pg & PAGE_COW) pg &= ~PAGE_RW; - if (in_swap_cache(pg)) { - swap_cache_invalidate(pg); + if (delete_from_swap_cache(pg)) pg |= PAGE_DIRTY; - } *new_page_table = pg; *old_page_table = pg; mem_map[MAP_NR(pg)]++; @@ -586,7 +584,7 @@ if (old_page & PAGE_RW) goto end_wp_page; vma->vm_task->mm->min_flt++; - prot = (old_page & ~PAGE_MASK) | PAGE_RW; + prot = (old_page & ~PAGE_MASK) | PAGE_RW | PAGE_DIRTY; old_page &= PAGE_MASK; if (mem_map[MAP_NR(old_page)] != 1) { if (new_page) { @@ -604,7 +602,7 @@ invalidate(); return; } - *(unsigned long *) pte |= PAGE_RW; + *(unsigned long *) pte |= PAGE_RW | PAGE_DIRTY; invalidate(); if (new_page) free_page(new_page); @@ -657,6 +655,8 @@ goto check_wp_fault_by_hand; for (;;) { struct vm_area_struct * next; + if (!(vma->vm_page_prot & PAGE_USER)) + goto bad_area; if (type != VERIFY_READ && !(vma->vm_page_prot & (PAGE_COW | PAGE_RW))) goto bad_area; if (vma->vm_end - start >= size) @@ -765,7 +765,7 @@ return 0; from |= PAGE_DIRTY; *(unsigned long *) from_page = from; - swap_cache_invalidate(from); + delete_from_swap_cache(from); invalidate(); } mem_map[MAP_NR(from)]++; @@ -1000,8 +1000,10 @@ if (bit < 32) current->screen_bitmap |= 1 << bit; } + if (!(vma->vm_page_prot & PAGE_USER)) + goto bad_area; if (error_code & PAGE_PRESENT) { - if ((vma->vm_page_prot & (PAGE_RW | PAGE_COW | PAGE_PRESENT)) == PAGE_PRESENT) + if (!(vma->vm_page_prot & (PAGE_RW | PAGE_COW))) goto bad_area; #ifdef CONFIG_TEST_VERIFY_AREA if (regs->cs == KERNEL_CS) @@ -1010,8 +1012,6 @@ do_wp_page(vma, address, error_code); return; } - if (!(vma->vm_page_prot & PAGE_PRESENT)) - goto bad_area; do_no_page(vma, address, error_code); return; diff -u --recursive --new-file v1.1.34/linux/mm/mmap.c linux/mm/mmap.c --- v1.1.34/linux/mm/mmap.c Fri Jul 22 16:41:03 1994 +++ linux/mm/mmap.c Sun Jul 24 17:39:47 1994 @@ -21,24 +21,21 @@ unsigned long); /* * description of effects of mapping type and prot in current implementation. - * this is due to the current handling of page faults in memory.c. the expected + * this is due to the limited x86 page protection hardware. The expected * behavior is in parens: * * map_type prot * PROT_NONE PROT_READ PROT_WRITE PROT_EXEC - * MAP_SHARED r: (no) yes r: (yes) yes r: (no) yes r: (no) no - * w: (no) yes w: (no) copy w: (yes) yes w: (no) no - * x: (no) no x: (no) no x: (no) no x: (yes) no + * MAP_SHARED r: (no) no r: (yes) yes r: (no) yes r: (no) yes + * w: (no) no w: (no) no w: (yes) yes w: (no) no + * x: (no) no x: (no) yes x: (no) yes x: (yes) yes * - * MAP_PRIVATE r: (no) yes r: (yes) yes r: (no) yes r: (no) no - * w: (no) copy w: (no) copy w: (copy) copy w: (no) no - * x: (no) no x: (no) no x: (no) no x: (yes) no + * MAP_PRIVATE r: (no) no r: (yes) yes r: (no) yes r: (no) yes + * w: (no) no w: (no) no w: (copy) copy w: (no) no + * x: (no) no x: (no) yes x: (no) yes x: (yes) yes * */ -#define CODE_SPACE(addr) \ - (PAGE_ALIGN(addr) < current->start_code + current->end_code) - int do_mmap(struct file * file, unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long off) { @@ -116,8 +113,8 @@ mask |= PAGE_COPY; else mask |= PAGE_SHARED; - if (!mask) - return -EINVAL; + if (!mask) /* PROT_NONE */ + mask = PAGE_PRESENT; /* none of PAGE_USER, PAGE_RW, PAGE_COW */ do_munmap(addr, len); /* Clear old maps */ diff -u --recursive --new-file v1.1.34/linux/mm/swap.c linux/mm/swap.c --- v1.1.34/linux/mm/swap.c Sat Jul 23 17:38:38 1994 +++ linux/mm/swap.c Sun Jul 24 17:39:47 1994 @@ -8,7 +8,6 @@ * This file should contain most things doing the swapping from/to disk. * Started 18.12.91 */ -#define SWAP_CACHE_INFO #include #include @@ -18,6 +17,7 @@ #include #include #include +#include #include /* for cli()/sti() */ #include @@ -49,68 +49,30 @@ extern int shm_swap (int); unsigned long *swap_cache; -static unsigned long swap_cache_size; #ifdef SWAP_CACHE_INFO -static unsigned long add_calls_total = 0; -static unsigned long add_calls_success = 0; -static unsigned long del_calls_total = 0; -static unsigned long del_calls_success = 0; -static unsigned long find_calls_total = 0; -static unsigned long find_calls_success = 0; +unsigned long swap_cache_add_total = 0; +unsigned long swap_cache_add_success = 0; +unsigned long swap_cache_del_total = 0; +unsigned long swap_cache_del_success = 0; +unsigned long swap_cache_find_total = 0; +unsigned long swap_cache_find_success = 0; -extern inline void show_swap_cache_info (void) +extern inline void show_swap_cache_info(void) { printk("Swap cache: add %ld/%ld, delete %ld/%ld, find %ld/%ld\n", - add_calls_total, add_calls_success, - del_calls_total, del_calls_success, - find_calls_total, find_calls_success); + swap_cache_add_total, swap_cache_add_success, + swap_cache_del_total, swap_cache_del_success, + swap_cache_find_total, swap_cache_find_success); } #endif -extern inline unsigned long init_swap_cache (unsigned long mem_start, - unsigned long mem_end) +extern inline int add_to_swap_cache(unsigned long addr, unsigned long entry) { - mem_start = (mem_start + 15) & ~15; - swap_cache = (unsigned long *) mem_start; - swap_cache_size = mem_end >> PAGE_SHIFT; - memset(swap_cache, 0, swap_cache_size * sizeof (unsigned long)); -#ifdef SWAP_CACHE_INFO - printk("%ld bytes for swap cache allocated\n", - swap_cache_size * sizeof(unsigned long)); -#endif - - return (unsigned long) (swap_cache + swap_cache_size); -} - -extern inline long find_in_swap_cache (unsigned long addr) -{ - unsigned long entry; - -#ifdef SWAP_CACHE_INFO - find_calls_total++; -#endif - __asm__ __volatile__ ( - "xchgl %0,%1\n" - : "=m" (swap_cache[addr >> PAGE_SHIFT]), - "=r" (entry) - : "0" (swap_cache[addr >> PAGE_SHIFT]), - "1" (0) - ); -#ifdef SWAP_CACHE_INFO - if (entry) - find_calls_success++; -#endif - - return entry; -} - -extern inline int add_to_swap_cache (unsigned long addr, unsigned long entry) -{ struct swap_info_struct * p = &swap_info[SWP_TYPE(entry)]; #ifdef SWAP_CACHE_INFO - add_calls_total++; + swap_cache_add_total++; #endif if ((p->flags & SWP_WRITEOK) == SWP_WRITEOK) { __asm__ __volatile__ ( @@ -124,39 +86,30 @@ printk("swap_cache: replacing non-NULL entry\n"); } #ifdef SWAP_CACHE_INFO - add_calls_success++; + swap_cache_add_success++; #endif return 1; } return 0; } - -extern inline int delete_from_swap_cache(unsigned long addr) +static unsigned long init_swap_cache(unsigned long mem_start, + unsigned long mem_end) { - unsigned long entry; - + unsigned long swap_cache_size; + + mem_start = (mem_start + 15) & ~15; + swap_cache = (unsigned long *) mem_start; + swap_cache_size = mem_end >> PAGE_SHIFT; + memset(swap_cache, 0, swap_cache_size * sizeof (unsigned long)); #ifdef SWAP_CACHE_INFO - del_calls_total++; + printk("%ld bytes for swap cache allocated\n", + swap_cache_size * sizeof(unsigned long)); #endif - __asm__ __volatile__ ( - "xchgl %0,%1\n" - : "=m" (swap_cache[addr >> PAGE_SHIFT]), - "=r" (entry) - : "0" (swap_cache[addr >> PAGE_SHIFT]), - "1" (0) - ); - if (entry) { -#ifdef SWAP_CACHE_INFO - del_calls_success++; -#endif - swap_free(entry); - return 1; - } - return 0; + + return (unsigned long) (swap_cache + swap_cache_size); } - void rw_swap_page(int rw, unsigned long entry, char * buf) { unsigned long type, offset; @@ -186,18 +139,44 @@ if (p->swap_device) { ll_rw_page(rw,p->swap_device,offset,buf); } else if (p->swap_file) { + struct inode *swapf = p->swap_file; unsigned int zones[8]; - unsigned int block; - int i, j; - - block = offset << (12 - p->swap_file->i_sb->s_blocksize_bits); - - for (i=0, j=0; j< PAGE_SIZE ; i++, j +=p->swap_file->i_sb->s_blocksize) - if (!(zones[i] = bmap(p->swap_file,block++))) { - printk("rw_swap_page: bad swap file\n"); - return; + int i; + if (swapf->i_op->bmap == NULL + && swapf->i_op->smap != NULL){ + /* + With MsDOS, we use msdos_smap which return + a sector number (not a cluster or block number). + It is a patch to enable the UMSDOS project. + Other people are working on better solution. + + It sounds like ll_rw_swap_file defined + it operation size (sector size) based on + PAGE_SIZE and the number of block to read. + So using bmap ou smap should work even if + smap will requiered more blocks. + */ + int j; + unsigned int block = offset << 3; + + for (i=0, j=0; j< PAGE_SIZE ; i++, j += 512){ + if (!(zones[i] = swapf->i_op->smap(swapf,block++))) { + printk("rw_swap_page: bad swap file\n"); + return; + } } - ll_rw_swap_file(rw,p->swap_file->i_dev, zones, i,buf); + }else{ + int j; + unsigned int block = offset + << (12 - swapf->i_sb->s_blocksize_bits); + + for (i=0, j=0; j< PAGE_SIZE ; i++, j +=swapf->i_sb->s_blocksize) + if (!(zones[i] = bmap(swapf,block++))) { + printk("rw_swap_page: bad swap file\n"); + return; + } + } + ll_rw_swap_file(rw,swapf->i_dev, zones, i,buf); } else printk("re_swap_page: no swap file or device\n"); if (offset && !clear_bit(offset,p->swap_lockmap))