Commit f48455d2 authored by Linus Torvalds's avatar Linus Torvalds

Import 1.1.35

parent 57dabd37
......@@ -53,7 +53,6 @@ S: The Netherlands
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
......
VERSION = 1
PATCHLEVEL = 1
SUBLEVEL = 34
SUBLEVEL = 35
all: Version zImage
......
......@@ -150,6 +150,7 @@ bool 'Extended fs support' CONFIG_EXT_FS n
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 @@ fi
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
......
......@@ -58,7 +58,7 @@ OBJS := $(OBJS) psaux.o
SRCS := $(SRCS) psaux.c
endif
ifdef CONFIG_TAPE_QIC02
ifdef CONFIG_QIC02_TAPE
OBJS := $(OBJS) tpqic02.o
SRCS := $(SRCS) tpqic02.c
endif
......
......@@ -156,7 +156,7 @@ static struct {
/* 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 @@ static void set_mode(int currcons, int on_off)
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 @@ static inline short limit(const int v, const int l, const int u)
}
/* 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(). */
......
......@@ -411,8 +411,8 @@ long chr_dev_init(long mem_start, long mem_end)
#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
......
/* $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 <linux/config.h>
/* skip this driver if not required for this configuration */
#if CONFIG_TAPE_QIC02
#if CONFIG_QIC02_TAPE
/*
#define TDEBUG
......@@ -171,29 +219,45 @@
#include <asm/segment.h>
/* 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 @@ static char rcs_date[] = "$Date: 1993/06/18 19:04:33 $";
* 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_read_eof = NO; /* set when we've done that */
static flag reported_write_eof = NO;
#ifdef TP_HAVE_SEEK
/* This is for doing `mt seek <blocknr>' */
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 @@ static int mode_access; /* access mode: READ or WRITE */
/* 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 @@ static int mode_access; /* access mode: READ or WRITE */
* 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 @@ static char *format_names[] = {
* "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 @@ static struct exception_list_type {
/* 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 inline void byte_swap_w(volatile unsigned short * w)
*/
static void ifc_init(void)
{
#if TAPE_QIC02_IFC == WANGTEK
if (QIC02_TAPE_IFC == WANGTEK) /* || (QIC02_TAPE_IFC == EVEREX) */ {
ctlbits = WT_CTL_ONLINE; /* online */
outb_p(ctlbits, QIC_CTL_PORT);
outb_p(ctlbits, QIC02_CTL_PORT);
#elif TAPE_QIC02_IFC == ARCHIVE
} else if (QIC02_TAPE_IFC == ARCHIVE) {
ctlbits = 0; /* no interrupts yet */
outb_p(ctlbits, QIC_CTL_PORT);
outb_p(ctlbits, QIC02_CTL_PORT);
outb_p(0, AR_RESET_DMA_PORT); /* dummy write to reset DMA */
#else
# error No valid interface card specified
#endif
} 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 @@ static void report_exception(unsigned n)
* 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 @@ static int decode_exception_nr(short s) /* s must be short, because of sign-exte
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 @@ static void report_error(int s)
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 @@ static void report_error(int s)
* 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 @@ static void handle_exception(int exnr, int exbits)
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 void handle_exception(int exnr, int exbits)
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 @@ static int tape_reset(int verbose)
{
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 @@ static int tape_reset(int verbose)
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 @@ static int tape_reset(int verbose)
/* 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 @@ static int notify_cmd(char cmd, short ignore_ex)
{
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 @@ static int wait_for_ready(time_t timeout)
* 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 @@ static int wait_for_ready(time_t timeout)
timeout -= spin_t;
spin_t += jiffies;
while (((stat = inb_p(QIC_STAT_PORT) & QIC_STAT_MASK) == QIC_STAT_MASK) && (jiffies<spin_t))
while (((stat = inb_p(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_STAT_MASK) && (jiffies<spin_t))
schedule(); /* don't waste all the CPU time */
if ((stat & QIC_STAT_READY) == 0)
if ((stat & QIC02_STAT_READY) == 0)
return TE_OK;
/* If we reach this point, we probably need to wait much longer, or
......@@ -674,7 +748,7 @@ static int wait_for_ready(time_t timeout)
TPQDEB({printk("wait_for_ready: additional timeout: %d\n", spin_t);})
/* not ready and no exception && timeout not expired yet */
while (((stat = inb_p(QIC_STAT_PORT) & QIC_STAT_MASK) == QIC_STAT_MASK) && (jiffies<spin_t)) {
while (((stat = inb_p(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_STAT_MASK) && (jiffies<spin_t)) {
/* be `nice` to other processes on long operations... */
current->timeout = jiffies + 30; /* nap 0.30 sec between checks, */
current->state = TASK_INTERRUPTIBLE;
......@@ -682,13 +756,13 @@ static int wait_for_ready(time_t timeout)
}
/* 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 @@ static int wait_for_ready(time_t timeout)
} /* 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 @@ static int send_qic02_cmd(int cmd, time_t timeout, int ignore_ex)
{
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 @@ static int send_qic02_cmd(int cmd, time_t timeout, int ignore_ex)
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 @@ static int rdstatus(char *stp, unsigned size, char qcmd)
* 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 @@ static int rdstatus(char *stp, unsigned size, char qcmd)
*/
if (TP_DIAGS(current_tape_dev))
printk(TPQIC_NAME ": reading status bytes: ");
printk(TPQIC02_NAME ": reading status bytes: ");
for (q=stp; q<stp+size; q++)
{
do s = inb_p(QIC_STAT_PORT);
while ((s & QIC_STAT_MASK) == QIC_STAT_MASK); /* wait for ready or exception */
do s = inb_p(QIC02_STAT_PORT);
while ((s & QIC02_STAT_MASK) == QIC02_STAT_MASK); /* wait for ready or exception */
if ((s & QIC_STAT_EXCEPTION) == 0) { /* if exception */
tpqputs("rdstatus: exception error");
if ((s & QIC02_STAT_EXCEPTION) == 0) { /* if exception */
tpqputs(TPQD_ALWAYS, "rdstatus: exception error");
ioctl_status.mt_erreg = 0; /* dunno... */
return TE_NS; /* error, shouldn't happen... */
}
*q = inb_p(QIC_DATA_PORT); /* read status byte */
*q = inb_p(QIC02_DATA_PORT); /* read status byte */
if (TP_DIAGS(current_tape_dev))
printk("[%1d]=0x%x ", q-stp, (unsigned) (*q) & 0xff);
outb_p(ctlbits | QIC_CTL_REQUEST, QIC_CTL_PORT); /* set request */
outb_p(ctlbits | QIC02_CTL_REQUEST, QIC02_CTL_PORT); /* set request */
while ((inb_p(QIC_STAT_PORT) & QIC_STAT_READY) == 0); /* wait for not ready */
while ((inb_p(QIC02_STAT_PORT) & QIC02_STAT_READY) == 0); /* wait for not ready */
udelay(22); /* delay >20 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 @@ static int rdstatus(char *stp, unsigned size, char qcmd)
* 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 @@ static int get_ext_status3(void)
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; i<sizeof(vus); i++) {
if ( i % 8 == 0 )
printk("\n" TPQIC_NAME ": %2d:");
printk("\n" TPQIC02_NAME ": %2d:");
printk(" %2x", vus[i] & 0xff);
}
printk("\n");
......@@ -892,26 +968,27 @@ static int tp_sense(int ignore)
unsigned err = 0, exnr = 0, gs = 0;
static void finish_rw(int cmd);
printk(TPQIC_NAME ": tp_sense(ignore=0x%x) enter\n", ignore);
if (TPQDBG(SENSE_TEXT))
printk(TPQIC02_NAME ": tp_sense(ignore=0x%x) enter\n", ignore);
/* sense() is not allowed during a read or write cycle */
if (doing_write == YES)
tpqputs("Warning: File Mark inserted because of sense() request");
tpqputs(TPQD_ALWAYS, "Warning: File Mark inserted because of sense() request");
/* The extra test is to avoid calling finish_rw during booting */
if ((doing_read!=NO) || (doing_write!=NO))
finish_rw(QCMD_RD_STAT);
if (get_status((char *) &tperror) != TE_OK) {
tpqputs("tp_sense: could not read tape drive status");
if (get_status(&tperror) != TE_OK) {
tpqputs(TPQD_ALWAYS, "tp_sense: could not read tape drive status");
return TE_ERR;
}
err = tperror.exs; /* get exception status bits */
if (err & (TP_ST0|TP_ST1))
printk(TPQIC_NAME ": tp_sense: status: %x, error count: %d, underruns: %d\n",
printk(TPQIC02_NAME ": tp_sense: status: %x, error count: %d, underruns: %d\n",
tperror.exs, tperror.dec, tperror.urc);
else
printk(TPQIC_NAME ": tp_sense: no errors at all, soft error count: %d, underruns: %d\n",
else if ((tperror.dec!=0) || (tperror.urc!=0) || TPQDBG(SENSE_CNTS))
printk(TPQIC02_NAME ": tp_sense: no hard errors, soft error count: %d, underruns: %d\n",
tperror.dec, tperror.urc);
/* Set generic status. HP-UX defines these, but some extra would
......@@ -951,7 +1028,8 @@ static int tp_sense(int ignore)
ioctl_status.mt_dsreg = tperror.exs; /* "drive status" */
ioctl_status.mt_erreg = tperror.dec; /* "sense key error" */
if (err!=0) {
if (err & (TP_ST0|TP_ST1)) {
/* My Wangtek occasionally reports `status' 1212 which should be ignored. */
exnr = decode_exception_nr(err);
handle_exception(exnr, err); /* update driver state wrt drive status */
report_exception(exnr);
......@@ -972,13 +1050,14 @@ static int wait_for_rewind(time_t timeout)
{
int stat;
stat = inb(QIC_STAT_PORT) & QIC_STAT_MASK;
printk(TPQIC_NAME ": Waiting for (re-)wind to finish: stat=0x%x\n", stat);
stat = inb(QIC02_STAT_PORT) & QIC02_STAT_MASK;
if (TPQDBG(REWIND))
printk(TPQIC02_NAME ": Waiting for (re-)wind to finish: stat=0x%x\n", stat);
stat = wait_for_ready(timeout);
if (stat != TE_OK) {
tpqputs("(re-) winding failed\n");
tpqputs(TPQD_ALWAYS, "(re-) winding failed\n");
}
return stat;
} /* wait_for_rewind */
......@@ -995,8 +1074,8 @@ static int ll_do_qic_cmd(int cmd, time_t timeout)
{
int stat;
if (status_dead) {
tpqputs("Drive is dead. Do a `mt reset`.");
if (status_dead == YES) {
tpqputs(TPQD_ALWAYS, "Drive is dead. Do a `mt reset`.");
return -ENXIO; /* User should do an MTRESET. */
}
......@@ -1008,14 +1087,14 @@ static int ll_do_qic_cmd(int cmd, time_t timeout)
stat = TE_OK;
}
if (stat != TE_OK) {
printk(TPQIC_NAME ": ll_do_qic_cmd(%x, %ld) failed\n", cmd, timeout);
printk(TPQIC02_NAME ": ll_do_qic_cmd(%x, %ld) failed\n", cmd, (long) timeout);
return -EIO;
}
#if OBSOLETE
/* wait for ready since it may not be active immediately after reading status */
while ((inb_p(QIC_STAT_PORT) & QIC_STAT_READY) != 0);
while ((inb_p(QIC02_STAT_PORT) & QIC02_STAT_READY) != 0);
#endif
stat = send_qic02_cmd(cmd, timeout, 0); /* (checks for exceptions) */
......@@ -1044,14 +1123,14 @@ static int ll_do_qic_cmd(int cmd, time_t timeout)
/* sense() will set eof/eom as required */
if (stat==TE_EX) {
if (tp_sense(TP_WRP|TP_BOM|TP_EOM|TP_FIL)!=TE_OK) {
printk(TPQIC_NAME ": Exception persist in ll_do_qic_cmd[1](%x, %ld)", cmd, timeout);
printk(TPQIC02_NAME ": Exception persist in ll_do_qic_cmd[1](%x, %ld)", cmd, (long) timeout);
status_dead = YES;
return -ENXIO;
/* if rdstatus fails too, we're in trouble */
}
}
else if (stat!=TE_OK) {
printk(TPQIC_NAME ": ll_do_qic_cmd: send_qic02_cmd failed, stat = 0x%x\n", stat);
printk(TPQIC02_NAME ": ll_do_qic_cmd: send_qic02_cmd failed, stat = 0x%x\n", stat);
return -EIO; /*** -EIO is probably not always appropriate */
}
......@@ -1065,7 +1144,7 @@ static int ll_do_qic_cmd(int cmd, time_t timeout)
if (tp_sense((cmd==QCMD_SEEK_EOD ? /*****************************/
TP_EOR|TP_NDT|TP_UDA|TP_BNL|TP_WRP|TP_BOM|TP_EOM|TP_FIL :
TP_WRP|TP_BOM|TP_EOM|TP_FIL))!=TE_OK) {
printk(TPQIC_NAME ": Exception persist in ll_do_qic_cmd[2](%x, %ld)\n", cmd, timeout);
printk(TPQIC02_NAME ": Exception persist in ll_do_qic_cmd[2](%x, %ld)\n", cmd, (long) timeout);
if (cmd!=QCMD_RD_FM)
status_dead = YES;
return -ENXIO;
......@@ -1073,7 +1152,7 @@ static int ll_do_qic_cmd(int cmd, time_t timeout)
}
}
else if (stat!=TE_OK) {
printk(TPQIC_NAME ": ll_do_qic_cmd %x: wait failed, stat == 0x%x\n", cmd, stat);
printk(TPQIC02_NAME ": ll_do_qic_cmd %x: wait failed, stat == 0x%x\n", cmd, stat);
return -EIO;
}
return 0;
......@@ -1113,12 +1192,19 @@ static void terminate_read(int cmd)
/* if the command is a RFM, there is no need to do this
* because a RFM will legally terminate the read-cycle.
*/
tpqputs("terminating pending read-cycle");
tpqputs(TPQD_ALWAYS, "terminating pending read-cycle");
/* I'm not too sure about this part -- hhb */
if (QIC02_TAPE_IFC == MOUNTAIN) {
/* Mountain reference says can terminate by de-asserting online */
ctlbits &= ~MTN_QIC02_CTL_ONLINE;
}
if (tp_sense(TP_FIL|TP_EOM|TP_WRP) != TE_OK) {
tpqputs("finish_rw[read1]: ignore the 2 lines above");
tpqputs(TPQD_ALWAYS, "finish_rw[read1]: ignore the 2 lines above");
if (is_exception()) {
if (tp_sense(TP_ILL|TP_FIL|TP_EOM|TP_WRP) != TE_OK)
tpqputs("finish_rw[read2]: read cycle error");
tpqputs(TPQD_ALWAYS, "finish_rw[read2]: read cycle error");
}
}
}
......@@ -1137,7 +1223,7 @@ static void terminate_write(int cmd)
/* finish off write cycle */
stat = ll_do_qic_cmd(QCMD_WRT_FM, TIM_M);
if (stat != TE_OK)
tpqputs("Couldn't finish write cycle properly");
tpqputs(TPQD_ALWAYS, "Couldn't finish write cycle properly");
(void) tp_sense(0);
}
/* If there is an EOF token waiting to be returned to
......@@ -1153,7 +1239,7 @@ static void terminate_write(int cmd)
static void finish_rw(int cmd)
{
if (wait_for_ready(TIM_S) != TE_OK) {
tpqputs("error: drive not ready in finish_rw() !");
tpqputs(TPQD_ALWAYS, "error: drive not ready in finish_rw() !");
return;
}
terminate_read(cmd);
......@@ -1173,10 +1259,10 @@ static int do_qic_cmd(int cmd, time_t timeout)
finish_rw(cmd);
if (need_rewind) {
tpqputs("Rewinding tape...");
tpqputs(TPQD_REWIND, "Rewinding tape...");
stat = ll_do_qic_cmd(QCMD_REWIND, TIM_R);
if (stat != 0) {
printk(TPQIC_NAME ": rewind failed in do_qic_cmd(). stat=0x%2x", stat);
printk(TPQIC02_NAME ": rewind failed in do_qic_cmd(). stat=0x%2x", stat);
return stat;
}
need_rewind = NO;
......@@ -1208,53 +1294,52 @@ static int do_ioctl_cmd(int cmd)
return (tape_reset(1)==TE_OK)? 0 : -EIO;
case MTFSF:
tpqputs("MTFSF forward searching filemark");
tpqputs(TPQD_IOCTLS, "MTFSF forward searching filemark");
if ((mode_access==WRITE) && status_bytes_wr)
return -EACCES;
return do_qic_cmd(QCMD_RD_FM, TIM_F);
case MTBSF:
#ifdef TP_HAVE_BSF
tpqputs("MTBSF backward searching filemark -- optional command");
if (TP_HAVE_BSF) {
tpqputs(TPQD_IOCTLS, "MTBSF backward searching filemark -- optional command");
if ((mode_access==WRITE) && status_bytes_wr)
return -EACCES;
stat = do_qic_cmd(QCMD_RD_FM_BCK, TIM_F);
#else
tpqputs("MTBSF not supported");
} else {
stat = -ENXIO;
#endif
}
status_eom_detected = status_eof_detected = NO;
return stat;
case MTFSR:
#ifdef TP_HAVE_FSR /* This is an optional QIC-02 command */
tpqputs("MTFSR forward space record");
if (TP_HAVE_FSR) { /* This is an optional QIC-02 command */
tpqputs(TPQD_IOCTLS, "MTFSR forward space record");
if ((mode_access==WRITE) && status_bytes_wr)
return -EACCES;
stat = do_qic_cmd(QCMD_SPACE_FWD, TIM_F);
#else
} else {
/**** fake it by doing a read data block command? ******/
tpqputs("MTFSR not supported");
tpqputs(TPQD_IOCTLS, "MTFSR not supported");
stat = -ENXIO;
#endif
}
return stat;
case MTBSR:
#ifdef TP_HAVE_BSR /* This is an optional QIC-02 command */
if (TP_HAVE_BSR) { /* This is an optional QIC-02 command */
/* we need this for appending files with GNU tar!! */
tpqputs("MTFSR backward space record");
tpqputs(TPQD_IOCTLS, "MTFSR backward space record");
if ((mode_access==WRITE) && status_bytes_wr)
return -EACCES;
stat = do_qic_cmd(QCMD_SPACE_BCK, TIM_F);
#else
tpqputs("MTBSR not supported");
} else {
tpqputs(TPQD_IOCTLS, "MTBSR not supported");
stat = -ENXIO;
#endif
}
status_eom_detected = status_eof_detected = NO;
return stat;
case MTWEOF:
tpqputs("MTWEOF write eof mark");
tpqputs(TPQD_IOCTLS, "MTWEOF write eof mark");
/* Plain GNU mt(1) 2.2 uses read-only mode for writing FM. :-( */
if (mode_access==READ)
return -EACCES;
......@@ -1266,14 +1351,14 @@ static int do_ioctl_cmd(int cmd)
/* not sure what to do with status_bytes when WFM should fail */
case MTREW:
tpqputs("MTREW rewinding tape");
tpqputs(TPQD_IOCTLS, "MTREW rewinding tape");
if ((mode_access==WRITE) && status_bytes_wr)
return -EACCES;
status_eom_detected = status_eof_detected = NO;
return do_qic_cmd(QCMD_REWIND, TIM_R);
case MTOFFL:
tpqputs("MTOFFL rewinding & going offline"); /*---*/
tpqputs(TPQD_IOCTLS, "MTOFFL rewinding & going offline"); /*---*/
/******* What exactly are we supposed to do, to take it offline????
*****/
/* Doing a drive select will clear (unlock) the current drive.
......@@ -1287,12 +1372,12 @@ static int do_ioctl_cmd(int cmd)
return stat;
case MTNOP:
tpqputs("MTNOP setting status only");
tpqputs(TPQD_IOCTLS, "MTNOP setting status only");
/********** should do `read position' for drives that support it **********/
return (tp_sense(-1)==TE_OK)? 0 : -EIO; /**** check return codes ****/
case MTRETEN:
tpqputs("MTRETEN retension tape");
tpqputs(TPQD_IOCTLS, "MTRETEN retension tape");
if ((mode_access==WRITE) && status_bytes_wr)
return -EACCES;
status_eom_detected = status_eof_detected = NO;
......@@ -1303,7 +1388,7 @@ static int do_ioctl_cmd(int cmd)
* we shouldn't skip the FM. Tricky.
* Maybe use RD_FM_BCK, then do a SPACE_FWD?
*/
tpqputs("MTBSFM not supported");
tpqputs(TPQD_IOCTLS, "MTBSFM not supported");
if ((mode_access==WRITE) && status_bytes_wr)
return -EACCES;
return -ENXIO;
......@@ -1316,7 +1401,7 @@ static int do_ioctl_cmd(int cmd)
* Maybe use RD_FM, then RD_FM_BCK, but not all
* drives will support that!
*/
tpqputs("MTFSFM not supported");
tpqputs(TPQD_IOCTLS, "MTFSFM not supported");
if ((mode_access==WRITE) && status_bytes_wr)
return -EACCES;
return -ENXIO;
......@@ -1326,15 +1411,13 @@ static int do_ioctl_cmd(int cmd)
* another file to the end, such that it would append
* after the last FM on tape.
*/
tpqputs("MTEOM search for End Of recorded Media");
tpqputs(TPQD_IOCTLS, "MTEOM search for End Of recorded Media");
if ((mode_access==WRITE) && status_bytes_wr)
return -EACCES;
#ifdef TP_HAVE_EOD
if (TP_HAVE_EOD) {
/* Use faster seeking when possible.
* This requires the absence of data beyond the EOM.
*/
# if TAPE_QIC02_DRIVE == MT_ISWT5150
/* It seems that my drive does not always perform the
* It seems that my drive does not always perform the
* SEEK_EOD correctly, unless it is preceded by a
* rewind command.
*/
......@@ -1344,11 +1427,9 @@ static int do_ioctl_cmd(int cmd)
stat = do_qic_cmd(QCMD_REWIND, TIM_R);
if (stat)
return stat;
# endif
stat = do_qic_cmd(QCMD_SEEK_EOD, TIM_F);
/* After a successful seek, TP_EOR should be returned */
#else
} else {
/* else just seek until the drive returns exception "No Data" */
stat = 0;
while ((stat==0) && (!status_eom_detected)) {
......@@ -1356,66 +1437,66 @@ static int do_ioctl_cmd(int cmd)
}
if (tperror.exs & TP_NDT)
return 0;
#endif
}
return stat;
case MTERASE:
tpqputs("MTERASE -- ERASE TAPE !");
tpqputs(TPQD_IOCTLS, "MTERASE -- ERASE TAPE !");
if ((tperror.exs & TP_ST0) && (tperror.exs & TP_WRP)) {
tpqputs("Cartridge is write-protected.");
tpqputs(TPQD_ALWAYS, "Cartridge is write-protected.");
return -EACCES;
} else {
time_t t = jiffies;
/* give user a few seconds to pull out tape */
while (jiffies - t < 3*HZ)
schedule();
}
/* Plain GNU mt(1) 2.2 erases a tape in O_RDONLY. :-( */
if (mode_access==READ)
return -EACCES;
/* don't bother writing filemark */
/* give user a few seconds to pull out tape */
while (jiffies - t < 4*HZ)
schedule();
}
/* don't bother writing filemark first */
status_eom_detected = status_eof_detected = NO;
return do_qic_cmd(QCMD_ERASE, TIM_R);
case MTRAS1:
#ifdef TP_HAVE_RAS1
tpqputs("MTRAS1: non-destructive self test");
if (TP_HAVE_RAS1) {
tpqputs(TPQD_IOCTLS, "MTRAS1: non-destructive self test");
stat = do_qic_cmd(QCMD_SELF_TST1, TIM_R);
if (stat != 0) {
tpqputs("RAS1 failed");
tpqputs(TPQD_ALWAYS, "RAS1 failed");
return stat;
}
return (tp_sense(0)==TE_OK)? 0 : -EIO; /* get_ext_status3(); */
#else
tpqputs("RAS1 not supported");
}
tpqputs(TPQD_IOCTLS, "RAS1 not supported");
return -ENXIO;
#endif
case MTRAS2:
#ifdef TP_HAVE_RAS2
tpqputs("MTRAS2: destructive self test");
if (TP_HAVE_RAS2) {
tpqputs(TPQD_IOCTLS, "MTRAS2: destructive self test");
stat = do_qic_cmd(QCMD_SELF_TST2, TIM_R);
if (stat != 0) {
tpqputs("RAS2 failed");
tpqputs(TPQD_ALWAYS, "RAS2 failed");
return stat;
}
return (tp_sense(0)==TE_OK)? 0 : -EIO; /* get_ext_status3(); */
#else
tpqputs("RAS2 not supported");
}
tpqputs(TPQD_IOCTLS, "RAS2 not supported");
return -ENXIO;
#endif
#ifdef TP_HAVE_SEEK
case MTSEEK:
tpqputs("MTSEEK seeking block");
if (TP_HAVE_SEEK && (QIC02_TAPE_IFC==ARCHIVE)) {
tpqputs(TPQD_IOCTLS, "MTSEEK seeking block");
if ((mode_access==WRITE) && status_bytes_wr)
return -EACCES;
/* NOTE: address (24 bits) is in seek_addr_buf[] */
return do_qic_cmd(QCMDV_SEEK_BLK, TIM_F);
#endif
return do_qic_cmd(AR_QCMDV_SEEK_BLK, TIM_F);
}
else
return -ENOTTY;
default:
return -ENOTTY;
......@@ -1444,28 +1525,38 @@ static int do_ioctl_cmd(int cmd)
static inline void dma_transfer(void)
{
#if TAPE_QIC02_IFC == WANGTEK
outb_p(WT_CTL_ONLINE, QIC_CTL_PORT); /* back to normal */
#elif TAPE_QIC02_IFC == ARCHIVE
if (QIC02_TAPE_IFC == WANGTEK) /* or EVEREX */
outb_p(WT_CTL_ONLINE, QIC02_CTL_PORT); /* back to normal */
else if (QIC02_TAPE_IFC == ARCHIVE)
outb_p(0, AR_RESET_DMA_PORT);
#endif
else /* QIC02_TAPE_IFC == MOUNTAIN */
outb_p(ctlbits, QIC02_CTL_PORT);
clear_dma_ff(TAPE_QIC02_DMA);
set_dma_mode(TAPE_QIC02_DMA, dma_mode);
set_dma_addr(TAPE_QIC02_DMA, buffaddr+dma_bytes_done); /* full address */
set_dma_count(TAPE_QIC02_DMA, TAPE_BLKSIZE);
clear_dma_ff(QIC02_TAPE_DMA);
set_dma_mode(QIC02_TAPE_DMA, dma_mode);
set_dma_addr(QIC02_TAPE_DMA, buffaddr+dma_bytes_done); /* full address */
set_dma_count(QIC02_TAPE_DMA, TAPE_BLKSIZE);
/* start tape DMA controller */
#if TAPE_QIC02_IFC == WANGTEK
outb_p(WT_CTL_DMA | WT_CTL_ONLINE, QIC_CTL_PORT); /* trigger DMA transfer */
#elif TAPE_QIC02_IFC == ARCHIVE
outb_p(AR_CTL_IEN | AR_CTL_DNIEN, QIC_CTL_PORT); /* enable interrupts again */
if (QIC02_TAPE_IFC == WANGTEK) /* or EVEREX */
outb_p(WT_CTL_DMA | WT_CTL_ONLINE, QIC02_CTL_PORT); /* trigger DMA transfer */
else if (QIC02_TAPE_IFC == ARCHIVE) {
outb_p(AR_CTL_IEN | AR_CTL_DNIEN, QIC02_CTL_PORT); /* enable interrupts again */
outb_p(0, AR_START_DMA_PORT); /* start DMA transfer */
/* In dma_end() AR_RESET_DMA_PORT is written too. */
#endif
} else /* QIC02_TAPE_IFC == MOUNTAIN */ {
inb(MTN_R_DESELECT_DMA_PORT);
outb_p(ctlbits | (MTN_CTL_EXC_IEN | MTN_CTL_DNIEN), QIC02_CTL_PORT);
outb_p(0, MTN_W_SELECT_DMA_PORT); /* start DMA transfer */
if (dma_mode == DMA_MODE_WRITE)
outb_p(0, MTN_W_DMA_WRITE_PORT); /* start DMA transfer */
}
/* start computer DMA controller */
enable_dma(TAPE_QIC02_DMA);
enable_dma(QIC02_TAPE_DMA);
/* block transfer should start now, jumping to the
* interrupt routine when done or an exception was detected.
*/
......@@ -1473,7 +1564,7 @@ static inline void dma_transfer(void)
/* start_dma() sets a DMA transfer up between the tape controller and
* the kernel tape_qic02_buf buffer.
* the kernel qic02_tape_buf buffer.
* Normally bytes_todo==dma_bytes_done at the end of a DMA transfer. If not,
* a filemark was read, or an attempt to write beyond the End Of Tape
* was made. [Or some other bad thing happened.]
......@@ -1484,8 +1575,8 @@ static int start_dma(short mode, unsigned long bytes_todo)
{
int stat;
TPQPUTS("start_dma() enter");
TPQDEB({printk(TPQIC_NAME ": doing_read==%d, doing_write==%d\n", doing_read, doing_write);})
tpqputs(TPQD_DEBUG, "start_dma() enter");
TPQDEB({printk(TPQIC02_NAME ": doing_read==%d, doing_write==%d\n", doing_read, doing_write);})
dma_bytes_done = 0;
dma_bytes_todo = bytes_todo;
......@@ -1508,7 +1599,7 @@ static int start_dma(short mode, unsigned long bytes_todo)
since we're only just starting a read/write it doesn't
matter some exceptions are cleared by reading the status;
we're only interested in CNI and WRP. -Eddy */
get_status((char *) &tperror);
get_status(&tperror);
#else
/* TP_CNI should now be handled in open(). -Hennus */
#endif
......@@ -1520,17 +1611,22 @@ static int start_dma(short mode, unsigned long bytes_todo)
#if OBSOLETE
/************* not needed iff rd_status() would wait for ready!!!!!! **********/
if (wait_for_ready(TIM_S) != TE_OK) { /*** not sure this is needed ***/
tpqputs("wait_for_ready failed in start_dma");
tpqputs(TPQD_ALWAYS, "wait_for_ready failed in start_dma");
return -EIO;
}
#endif
if (QIC02_TAPE_IFC == MOUNTAIN) {
/* Set control bits to select ONLINE during command */
ctlbits |= MTN_QIC02_CTL_ONLINE;
}
/* Tell the controller the data direction */
/* r/w, timeout medium, check exceptions, sets status_cmd_pending. */
stat = send_qic02_cmd((mode == WRITE)? QCMD_WRT_DATA : QCMD_RD_DATA, TIM_M, 0);
if (stat!=TE_OK) {
printk(TPQIC_NAME ": start_dma: init %s failed\n",
printk(TPQIC02_NAME ": start_dma: init %s failed\n",
(mode == WRITE)? "write" : "read");
(void) tp_sense(0);
return stat;
......@@ -1549,8 +1645,8 @@ static int start_dma(short mode, unsigned long bytes_todo)
doing_write = YES;
break;
default:
printk(TPQIC_NAME ": requested unknown mode %d\n", mode);
panic(TPQIC_NAME ": invalid mode in start_dma()");
printk(TPQIC02_NAME ": requested unknown mode %d\n", mode);
panic(TPQIC02_NAME ": invalid mode in start_dma()");
}
} else if (is_exception()) {
......@@ -1559,7 +1655,7 @@ static int start_dma(short mode, unsigned long bytes_todo)
*
* ******** this also affects EOF/EOT handling! ************
*/
tpqputs("detected exception in start_dma() while transfer in progress");
tpqputs(TPQD_ALWAYS, "detected exception in start_dma() while transfer in progress");
status_error = YES;
return TE_END;
}
......@@ -1597,21 +1693,30 @@ static void end_dma(unsigned long * bytes_done)
TPQPUTS("end_dma() enter");
disable_dma(TAPE_QIC02_DMA);
clear_dma_ff(TAPE_QIC02_DMA);
disable_dma(QIC02_TAPE_DMA);
clear_dma_ff(QIC02_TAPE_DMA);
#if TAPE_QIC02_IFC == WANGTEK
outb_p(WT_CTL_ONLINE, QIC_CTL_PORT); /* back to normal */
#elif TAPE_QIC02_IFC == ARCHIVE
if (QIC02_TAPE_IFC == WANGTEK) /* or EVEREX */
outb_p(WT_CTL_ONLINE, QIC02_CTL_PORT); /* back to normal */
else if (QIC02_TAPE_IFC == ARCHIVE)
outb_p(0, AR_RESET_DMA_PORT);
#endif
else /* QIC02_TAPE_IFC == MOUNTAIN */ {
/* Clear control bits, de-select ONLINE during tp_sense */
ctlbits &= ~MTN_QIC02_CTL_ONLINE;
}
stat = wait_for_ready(TIM_M);
if (status_error || (stat!=TE_OK)) {
tpqputs("DMA transfer exception");
tpqputs(TPQD_DMAX, "DMA transfer exception");
stat = tp_sense((dma_mode==READ)? TP_WRP : 0);
/* no return here -- got to clean up first! */
} else /* if (QIC02_TAPE_IFC == MOUNTAIN) */ {
outb_p(ctlbits, QIC02_CTL_PORT);
}
if (QIC02_TAPE_IFC == MOUNTAIN)
inb(MTN_R_DESELECT_DMA_PORT);
/* take the tape controller offline */
/* finish off DMA stuff */
......@@ -1635,15 +1740,15 @@ static void end_dma(unsigned long * bytes_done)
/*********** Below are the (public) OS-interface procedures ***********/
/* tape_qic02_times_out() is called when a DMA transfer doesn't complete
/* qic02_tape_times_out() is called when a DMA transfer doesn't complete
* quickly enough. Usually this means there is something seriously wrong
* with the hardware/software, but it could just be that the controller
* has decided to do a long rewind, just when I didn't expect it.
* Just try again.
*/
static void tape_qic02_times_out(void)
static void qic02_tape_times_out(void)
{
printk("time-out in %s driver\n", TPQIC_NAME);
printk("time-out in %s driver\n", TPQIC02_NAME);
if ((status_cmd_pending>0) || dma_mode) {
/* takes tooo long, shut it down */
status_dead = YES;
......@@ -1653,10 +1758,10 @@ static void tape_qic02_times_out(void)
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 @@ static void tape_qic02_times_out(void)
*/
/* 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 @@ static void tape_qic02_times_out(void)
* 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?" */
#endif
stat = inb(QIC02_STAT_PORT); /* Knock, knock */
if (QIC02_TAPE_IFC == ARCHIVE) { /* "Who's there?" */
if (((stat & (AR_STAT_DMADONE)) == 0) &&
((stat & (QIC_STAT_EXCEPTION)) != 0)) {
((stat & (QIC02_STAT_EXCEPTION)) != 0)) {
TIMERCONT;
return; /* "Linux with IRQ sharing" */
}
#endif
if ((stat & QIC_STAT_EXCEPTION) == 0) { /* exception occurred */
}
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 @@ static void tape_qic02_interrupt(int unused)
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 @@ static void tape_qic02_interrupt(int unused)
/* 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");
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;
}
#endif
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 @@ static void tape_qic02_interrupt(int unused)
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 @@ static int tape_qic02_lseek(struct inode * inode, struct file * file, off_t offs
* 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 @@ static int tape_qic02_read(struct inode * inode, struct file * filp, char * buf,
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 @@ static int tape_qic02_read(struct inode * inode, struct file * filp, char * buf,
/* 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 @@ static int tape_qic02_read(struct inode * inode, struct file * filp, char * buf,
/* 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 @@ static int tape_qic02_read(struct inode * inode, struct file * filp, char * buf,
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 @@ static int tape_qic02_read(struct inode * inode, struct file * filp, char * buf,
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 @@ static int tape_qic02_read(struct inode * inode, struct file * filp, char * buf,
}
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 @@ static int tape_qic02_read(struct inode * inode, struct file * filp, char * buf,
#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 @@ static int tape_qic02_read(struct inode * inode, struct file * filp, char * buf,
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 @@ static int tape_qic02_read(struct inode * inode, struct file * filp, char * buf,
* 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 @@ static int tape_qic02_write(struct inode * inode, struct file * filp, char * buf
* 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 @@ static int tape_qic02_write(struct inode * inode, struct file * filp, char * buf
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 @@ static int tape_qic02_write(struct inode * inode, struct file * filp, char * buf
***** 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 @@ static int tape_qic02_write(struct inode * inode, struct file * filp, char * buf
*/
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 @@ static int tape_qic02_write(struct inode * inode, struct file * filp, char * buf
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 @@ static int tape_qic02_write(struct inode * inode, struct file * filp, char * buf
* 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 @@ static int tape_qic02_open(struct inode * inode, struct file * filp)
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 @@ static int tape_qic02_open(struct inode * inode, struct file * filp)
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);
/* This is to avoid tape-changed problems (TP_CNI exception). */
if (is_exception()) {
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);
if (s != TE_OK) {
status_open = NO;
tpqputs("open: sense() failed after exception was detected");
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 @@ static int tape_qic02_open(struct inode * inode, struct file * filp)
* i.e. user wants to use tape in different format.
* [assuming single drive operation]
*/
tpqputs("Density minor bits have changed. Forcing rewind.");
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 @@ static int tape_qic02_open(struct inode * inode, struct file * filp)
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 @@ static int tape_qic02_open(struct inode * inode, struct file * filp)
current_tape_dev = dev;
need_rewind = NO;
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 @@ static int tape_qic02_open(struct inode * inode, struct file * filp)
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 @@ static void tape_qic02_release(struct inode * inode, struct file * filp)
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 @@ static int tape_qic02_ioctl(struct inode * inode, struct file * filp,
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 @@ static int tape_qic02_ioctl(struct inode * inode, struct file * filp,
/* 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<sizeof(qic02_tape_dynconf); i++)
put_fs_byte(*stp++, argp++);
return 0;
} else if (c == (MTIOCSETCONFIG & IOCCMD_MASK)) {
static int qic02_get_resources(void), qic02_release_resources(void);
/* One should always do a MTIOCGETCONFIG first, then update
* user-settings, then write back with MTIOCSETCONFIG.
* Re-open() the device before actual use to make sure
* everything is initialized.
*/
if (((iocmd & IOCSIZE_MASK) >> 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<sizeof(qic02_tape_dynconf); i++)
*stp++ = get_fs_byte(argp++);
if (status_zombie==NO)
qic02_release_resources(); /* and go zombie */
if (update_ifc_masks(qic02_tape_dynconf.ifc_type))
return -EIO;
if (qic02_get_resources())
return -ENXIO;
return 0;
}
if (status_zombie==YES) {
tpqputs(TPQD_ALWAYS, "Configs not set");
return -ENXIO;
}
#endif
if (c == (MTIOCTOP & IOCCMD_MASK)) {
/* Compare expected struct size and actual struct size. This
* is useful to catch programs compiled with old #includes.
*/
if (((iocmd & IOCSIZE_MASK) >> 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 @@ static int tape_qic02_ioctl(struct inode * inode, struct file * filp,
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 @@ static int tape_qic02_ioctl(struct inode * inode, struct file * filp,
/* 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 @@ static int tape_qic02_ioctl(struct inode * inode, struct file * filp,
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 @@ static int tape_qic02_ioctl(struct inode * inode, struct file * filp,
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 @@ static int tape_qic02_ioctl(struct inode * inode, struct file * filp,
for (i=0; i<sizeof(ioctl_tell); i++)
put_fs_byte(*stp++, argp++);
return 0;
#endif
} else
return -ENOTTY; /* Other cmds not supported. */
} /* tape_qic02_ioctl */
} /* qic02_tape_ioctl */
/* These are (most) of the interface functions: */
static struct file_operations tape_qic02_fops = {
tape_qic02_lseek, /* not allowed */
tape_qic02_read, /* read */
tape_qic02_write, /* write */
tape_qic02_readdir, /* not allowed */
static struct file_operations qic02_tape_fops = {
qic02_tape_lseek, /* not allowed */
qic02_tape_read, /* read */
qic02_tape_write, /* write */
qic02_tape_readdir, /* not allowed */
NULL, /* select ??? */
tape_qic02_ioctl, /* ioctl */
qic02_tape_ioctl, /* ioctl */
NULL, /* mmap not allowed */
tape_qic02_open, /* open */
tape_qic02_release, /* release */
qic02_tape_open, /* open */
qic02_tape_release, /* release */
NULL /* fsync */
};
......@@ -2491,8 +2802,8 @@ static struct file_operations tape_qic02_fops = {
* then dma_transfer() would have to disable interrupts explicitly.
* System load is high enough as it is :-(
*/
static struct sigaction tape_qic02_sigaction = {
tape_qic02_interrupt,
static struct sigaction qic02_tape_sigaction = {
qic02_tape_interrupt,
0,
SA_INTERRUPT,
NULL
......@@ -2509,105 +2820,159 @@ static inline unsigned long const align_buffer(unsigned long a, unsigned size)
}
/* init() is called from chr_dev_init() in kernel/chr_drv/mem.c */
long tape_qic02_init(long kmem_start)
/* Shouldn't this be a caddr_t ? */
static void qic02_release_resources(void)
{
free_irq(QIC02_TAPE_IRQ);
free_dma(QIC02_TAPE_DMA);
status_zombie = YES;
} /* qic02_release_resources */
printk(TPQIC_NAME ": IRQ %d, DMA %d, IO %xh, IFC %s, %s, %s\n",
TAPE_QIC02_IRQ, TAPE_QIC02_DMA, TAPE_QIC02_PORT,
#if TAPE_QIC02_IFC == WANGTEK
"Wangtek",
#elif TAPE_QIC02_IFC == ARCHIVE
"Archive",
#else
# error
#endif
rcs_revision, rcs_date);
static int qic02_get_resources(void)
{
/* First perform some checks. If one of them fails,
* the tape driver will not be registered to the system.
*/
if (QIC02_TAPE_IRQ>16) {
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 <hint.h>
/* 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 @@ long tape_qic02_init(long kmem_start)
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 */
......@@ -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 @@ static int tty_ioctl(struct inode * inode, struct file * file,
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;
......
......@@ -62,8 +62,6 @@ int autoirq_setup(int waittime)
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 @@ int autoirq_setup(int waittime)
}
/* Update our USED lists. */
irqs_used |= ~irq_handled;
irq_number = 0;
irq_bitmap = 0;
/* Hang out at least <waittime> jiffies waiting for bogus IRQ hits. */
while (timeout > jiffies)
......
......@@ -305,7 +305,7 @@ static inline unsigned char xchgb(unsigned char reg,
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;
}
......
......@@ -33,6 +33,9 @@ endif
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
......
......@@ -14,6 +14,7 @@
#include <linux/ext2_fs.h>
#include <linux/xia_fs.h>
#include <linux/msdos_fs.h>
#include <linux/umsdos_fs.h>
#include <linux/proc_fs.h>
#include <linux/nfs_fs.h>
#include <linux/iso_fs.h>
......@@ -52,6 +53,10 @@ asmlinkage int sys_setup(void)
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
register_filesystem(&(struct file_system_type)
......
......@@ -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)
......
......@@ -12,6 +12,7 @@
#include <linux/msdos_fs.h>
#include <linux/errno.h>
#include <linux/stat.h>
#include <linux/string.h>
static int msdos_dir_read(struct inode * inode,struct file * filp, char * buf,int count)
......@@ -19,7 +20,7 @@ static int msdos_dir_read(struct inode * inode,struct file * filp, char * buf,in
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 @@ struct inode_operations msdos_dir_inode_operations = {
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;
......@@ -80,20 +84,26 @@ static int msdos_readdir(struct inode *inode,struct file *filp,
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 @@ static int msdos_readdir(struct inode *inode,struct file *filp,
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;
......
......@@ -15,16 +15,11 @@
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <linux/string.h>
#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 @@ static struct file_operations msdos_file_operations = {
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 @@ struct inode_operations msdos_file_inode_operations = {
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,11 +69,18 @@ struct inode_operations msdos_file_inode_operations_no_bmap = {
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,
/*
Read a file into user space
*/
int msdos_file_read(
struct inode *inode,
struct file *filp,
char *buf,
int count)
{
char *start;
......@@ -91,7 +94,8 @@ static int msdos_file_read(struct inode *inode,struct file *filp,char *buf,
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,8 +135,13 @@ static int msdos_file_read(struct inode *inode,struct file *filp,char *buf,
return buf-start;
}
static int msdos_file_write(struct inode *inode,struct file *filp,char *buf,
/*
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;
......@@ -145,7 +154,8 @@ static int msdos_file_write(struct inode *inode,struct file *filp,char *buf,
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;
}
......@@ -211,7 +221,6 @@ static int msdos_file_write(struct inode *inode,struct file *filp,char *buf,
return buf-start;
}
void msdos_truncate(struct inode *inode)
{
int cluster;
......
/*
* 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 <linux/stat.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/shm.h>
#include <linux/errno.h>
#include <linux/mman.h>
#include <linux/string.h>
#include <linux/malloc.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <linux/msdos_fs.h>
/*
* 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;
}
......@@ -360,7 +360,11 @@ int msdos_rmdir(struct inode *dir,const char *name,int len)
}
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 @@ int msdos_unlink(struct inode *dir,const char *name,int len)
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 @@ int msdos_unlink(struct inode *dir,const char *name,int len)
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,
......
#
# 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
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!
#include <asm/system.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/head.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/ptrace.h>
#include <linux/mman.h>
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);
}
}
/*
* linux/fs/umsdos/dir.c
*
* Written 1993 by Jacques Gelinas
* Inspired from linux/fs/msdos/... : Werner Almesberger
*
* Extended MS-DOS directory handling functions
*/
#include <asm/segment.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/msdos_fs.h>
#include <linux/errno.h>
#include <linux/stat.h>
#include <linux/limits.h>
#include <linux/umsdos_fs.h>
#include <linux/malloc.h>
#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 */
};
/*
* linux/fs/umsdos/emd.c
*
* Written 1993 by Jacques Gelinas
*
* Extended MS-DOS directory handling functions
*/
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/kernel.h>
#include <asm/segment.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/msdos_fs.h>
#include <linux/umsdos_fs.h>
#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;
}
/*
* 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 <asm/segment.h>
#include <asm/system.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/msdos_fs.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <linux/msdos_fs.h>
#include <linux/umsdos_fs.h>
#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 */
};
/*
* linux/fs/umsdos/inode.c
*
* Written 1993 by Jacques Gelinas
* Inspired from linux/fs/msdos/... by Werner Almesberger
*
*/
#include <stdlib.h>
#include <linux/fs.h>
#include <linux/msdos_fs.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <asm/segment.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/stat.h>
#include <linux/umsdos_fs.h>
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;
}
/*
* linux/fs/umsdos/ioctl.c
*
* Written 1993 by Jacques Gelinas
*
* Extended MS-DOS ioctl directory handling functions
*/
#include <asm/segment.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/msdos_fs.h>
#include <linux/umsdos_fs.h>
#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;
}
/*
* 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 <linux/errno.h>
#include <linux/ctype.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/umsdos_fs.h>
/*
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; i<msdos_len; i++, pt++) *pt = lkp[(unsigned char)(*pt)];
*pt = '\0'; /* GLU C'est sur on a un 0 a la fin */
info->msdos_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 <CAH17@PHOENIX.CAMBRIDGE.AC.UK>
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]; i<start_ind_dev[base_len]; i++){
if (memcmp(info->fake.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; i<sizeof(tb)/sizeof(tb[0]); i++){
struct MANG_TEST *pttb = tb+i;
struct umsdos_info info;
int ok = umsdos_parse (pttb->fname,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<UMSDOS_MAXNAME ; i++){
rnew=umsdos_evalrecsize (i);
rold=umsdos_evalrecsize_old (i);
if (!(i%UMSDOS_REC_SIZE)){
printf ("\n%d:\t",i);
}
if (rnew!=rold){
printf ("**** %d newres: %d != %d \n", i, rnew, rold);
}else{
printf(".");
}
}
printf ("\nEnd of Testing.\n");
return 0;
}
#endif
/*
* linux/fs/umsdos/namei.c
*
* Written 1993 by Jacques Gelinas
* Inspired from linux/fs/msdos/... by Werner Almesberger
*
* Maintain and access the --linux alternate directory file.
*/
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/msdos_fs.h>
#include <linux/umsdos_fs.h>
#include <linux/malloc.h>
#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;
}
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
/*
* 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 <asm/segment.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/msdos_fs.h>
#include <linux/errno.h>
#include <linux/stat.h>
#include <linux/limits.h>
#include <linux/umsdos_fs.h>
#include <linux/malloc.h>
#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 */
};
/*
* 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 <asm/segment.h>
#include <asm/system.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/msdos_fs.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <linux/umsdos_fs.h>
#include <linux/malloc.h>
#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 */
};
......@@ -59,6 +59,7 @@ struct ext_dir_entry {
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_file_inode_operations;
extern struct inode_operations ext_dir_inode_operations;
extern struct inode_operations ext_symlink_inode_operations;
#endif /*__KERNEL__ */
#endif
......@@ -158,11 +158,14 @@ struct buffer_head {
#include <linux/ext2_fs_i.h>
#include <linux/hpfs_fs_i.h>
#include <linux/msdos_fs_i.h>
#include <linux/umsdos_fs_i.h>
#include <linux/iso_fs_i.h>
#include <linux/nfs_fs_i.h>
#include <linux/xia_fs_i.h>
#include <linux/sysv_fs_i.h>
#ifdef __KERNEL__
struct inode {
dev_t i_dev;
unsigned long i_ino;
......@@ -203,6 +206,7 @@ struct inode {
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 @@ struct inode_operations {
int (*bmap) (struct inode *,int);
void (*truncate) (struct inode *);
int (*permission) (struct inode *, int);
int (*smap) (struct inode *,int);
};
struct super_operations {
......@@ -333,8 +338,6 @@ struct file_system_type {
struct file_system_type * next;
};
#ifdef __KERNEL__
extern int register_filesystem(struct file_system_type *);
extern int unregister_filesystem(struct file_system_type *);
......
......@@ -117,22 +117,6 @@ struct iso_directory_record {
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,6 +144,22 @@ extern int find_rock_ridge_relocation(struct iso_directory_record *, struct inod
#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. */
......@@ -212,6 +212,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
......
......@@ -75,6 +75,8 @@ struct minix_dir_entry {
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,
......@@ -116,4 +118,6 @@ 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
......@@ -92,6 +92,8 @@ struct page_info {
};
/* end of planning stage */
#ifdef __KERNEL__
/*
* Free area management
*/
......@@ -221,21 +223,76 @@ extern unsigned short * mem_map;
#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
......@@ -4,7 +4,6 @@
/*
* The MS-DOS filesystem constants/structures
*/
#include <linux/fs.h>
#include <linux/stat.h>
#include <linux/fd.h>
......@@ -109,6 +108,7 @@ struct fat_cache {
#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_create(struct inode *dir,const char *name,int len,int mode,
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 @@ extern int msdos_notify_change(int flags,struct inode *inode);
/* 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
#ifndef _MSDOS_FS_I
#define _MSDOS_FS_I
#ifndef _LINUX_CONFIG_H
#include <linux/config.h>
#endif
#ifndef _LINUX_PIPE_FS_I_H
#include <linux/pipe_fs_i.h>
#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
......
/* $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 @@ struct mtop {
#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 @@ struct mtget {
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 @@ struct mtget {
#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 */
......@@ -123,7 +128,10 @@ struct mt_tape_info {
{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"}, \
......@@ -157,6 +165,12 @@ struct mtpos {
#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 @@ struct mtpos {
#define MT_ST_TWO_FM 0x10
#endif /* _LINUX_MTIO_H */
......@@ -54,7 +54,7 @@ typedef enum {
#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 @@ struct net_proto {
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 */
......@@ -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,
......@@ -113,4 +115,6 @@ extern struct inode_operations nfs_symlink_inode_operations;
extern int nfs_mmap(struct inode * inode, struct file * file,
unsigned long addr, size_t len, int prot, unsigned long off);
#endif /* __KERNEL__ */
#endif
......@@ -352,6 +352,8 @@ struct task_struct {
/* 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;
......@@ -620,4 +622,6 @@ extern struct desc_struct default_ldt;
:"m" (current->debugreg[register]) \
:"dx");
#endif /* __KERNEL__ */
#endif
......@@ -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
......
/* $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 <linux/config.h>
#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 <linux/mtio.h>
/* 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,84 +38,151 @@
#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 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_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_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 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_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! */
......@@ -118,91 +195,245 @@
*/
#endif
#endif /* !CONFIG_QIC02_DYNCONF */
/* 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 */
/* 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)
#define BLOCKS_BEYOND_EW 2 /* nr of blocks after Early Warning hole */
/* status register bits (Active LOW!) */
#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)
#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)
#define WT_QIC02_STAT_RESETMASK 0x07
#define WT_QIC02_STAT_RESETVAL (WT_QIC02_STAT_RESETMASK & ~WT_QIC02_STAT_EXCEPTION)
/* 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
/* 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
#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)
#define WT_CTL_DMA3 0x10 /* enable dma chan3 */
#define WT_CTL_DMA1 0x08 /* enable dma chan1 or chan2 */
/* 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 */
/* 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
#else
# 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 /* TAPE_QIC02_IFC */
# 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 /* !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 @@ struct tpstatus { /* sizeof(short)==2), LSB first */
#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 @@ struct tpstatus { /* sizeof(short)==2), LSB first */
#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<<TAPE_QIC02_TIMER)
#define TIMEROFF timer_active &= ~(1<<TAPE_QIC02_TIMER)
#define TIMERCONT timer_active |= (1<<TAPE_QIC02_TIMER)
#define TIMERON(t) timer_table[QIC02_TAPE_TIMER].expires = jiffies + (t); \
timer_active |= (1<<QIC02_TAPE_TIMER)
#define TIMEROFF timer_active &= ~(1<<QIC02_TAPE_TIMER)
#define TIMERCONT timer_active |= (1<<QIC02_TAPE_TIMER)
typedef char flag;
......@@ -365,10 +614,80 @@ typedef char flag;
#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 */
extern long tape_qic02_init(long); /* for kernel/mem.c */
#define BLOCKS_BEYOND_EW 2 /* nr of blocks after Early Warning hole */
#define BOGUS_IRQ 32009
/* structure for MTIOCGETCONFIG/MTIOCSETCONFIG primarily intended
* as an interim solution for QIC-02 until DDI is fully implemented.
*/
struct mtconfiginfo {
long mt_type; /* drive type */
long ifc_type; /* interface card type */
unsigned short irqnr; /* IRQ number to use */
unsigned short dmanr; /* DMA channel to use */
unsigned short port; /* IO port base address */
unsigned long debug; /* debugging flags */
unsigned have_dens:1;
unsigned have_bsf:1;
unsigned have_fsr:1;
unsigned have_bsr:1;
unsigned have_eod:1;
unsigned have_seek:1;
unsigned have_tell:1;
unsigned have_ras1:1;
unsigned have_ras2:1;
unsigned have_ras3:1;
unsigned have_qfa:1;
unsigned pad1:5;
char reserved[10];
};
#endif /* CONFIG_TAPE_QIC02 */
/* This is internal data, filled in based on the ifc_type field given
* by the user. Everex is mapped to Wangtek with a different
* `dma_enable_value', if dmanr==3.
*/
struct qic02_ccb {
long ifc_type;
unsigned short port_stat; /* Status port address */
unsigned short port_ctl; /* Control port address */
unsigned short port_cmd; /* Command port address */
unsigned short port_data; /* Data port address */
/* status register bits */
unsigned short stat_ready; /* drive ready */
unsigned short stat_exception; /* drive signals exception */
unsigned short stat_mask;
unsigned short stat_resetmask;
unsigned short stat_resetval;
/* control register bits */
unsigned short ctl_reset; /* reset drive */
unsigned short ctl_request; /* latch command */
/* This is used to change the DMA3 behaviour */
unsigned short dma_enable_value;
};
extern long qic02_tape_init(long); /* for mem.c */
#endif /* CONFIG_QIC02_TAPE */
#endif /* _LINUX_TPQIC02_H */
......@@ -5,6 +5,7 @@
* 'tty.h' defines some structures used by tty_io.c and some defines.
*/
#ifdef __KERNEL__
#include <linux/fs.h>
#include <linux/termios.h>
#include <linux/tqueue.h>
......@@ -13,6 +14,7 @@
#include <asm/system.h>
/*
* Note: don't mess with NR_PTYS until you understand the tty minor
* number allocation game...
......@@ -322,4 +324,5 @@ extern void unblank_screen(void);
extern int vt_ioctl(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg);
#endif /* __KERNEL__ */
#endif
#ifndef LINUX_UMSDOS_FS_H
#define LINUX_UMSDOS_FS_H
#define UMSDOS_VERSION 0
#define UMSDOS_RELEASE 3
#ifndef LINUX_FS_H
#include <linux/fs.h>
#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 <sys/stat.h>
#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 <linux/umsdos_fs.p>
#endif /* __KERNEL__ */
#endif
/* 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 */
#ifndef UMSDOS_FS_I_H
#define UMSDOS_FS_I_H
#ifndef _LINUX_TYPES_H
#include <linux/types.h>
#endif
#include <linux/msdos_fs_i.h>
#include <linux/pipe_fs_i.h>
/* #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 <linux/types.h>
#endif
#include <linux/msdos_fs_i.h>
#include <linux/pipe_fs_i.h>
/* #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
......@@ -500,6 +500,22 @@ void init(void)
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);
......
......@@ -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
};
......
......@@ -269,10 +269,8 @@ int copy_page_tables(struct task_struct * tsk)
}
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 @@ void do_wp_page(struct vm_area_struct * vma, unsigned long address,
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 @@ void do_wp_page(struct vm_area_struct * vma, unsigned long address,
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 @@ int verify_area(int type, const void * addr, unsigned long size)
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 @@ static int try_to_share(unsigned long to_address, struct vm_area_struct * to_are
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 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)
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 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)
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;
......
......@@ -21,24 +21,21 @@ static int anon_map(struct inode *, struct file *,
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 @@ int do_mmap(struct file * file, unsigned long addr, unsigned long len,
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 */
......
......@@ -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 <linux/mm.h>
#include <linux/sched.h>
......@@ -18,6 +17,7 @@
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/fs.h>
#include <asm/system.h> /* for cli()/sti() */
#include <asm/bitops.h>
......@@ -49,68 +49,30 @@ static struct swap_info_struct {
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;
extern inline void show_swap_cache_info (void)
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)
{
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)
{
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)
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,38 +86,29 @@ extern inline int add_to_swap_cache (unsigned long addr, unsigned long entry)
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++;
#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++;
printk("%ld bytes for swap cache allocated\n",
swap_cache_size * sizeof(unsigned long));
#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)
{
......@@ -186,18 +139,44 @@ void rw_swap_page(int rw, unsigned long entry, char * buf)
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;
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;
block = offset << (12 - p->swap_file->i_sb->s_blocksize_bits);
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;
}
}
}else{
int j;
unsigned int block = offset
<< (12 - swapf->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++))) {
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,p->swap_file->i_dev, zones, i,buf);
}
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))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment