#
# Code maturity level options
#
-CONFIG_EXPERIMENTAL=y
+# CONFIG_EXPERIMENTAL is not set
#
# Loadable module support
#
CONFIG_MODULES=y
-CONFIG_MODVERSIONS=y
-CONFIG_KMOD=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_KMOD is not set
#
# General setup
@@ -53,7+53,6 @@ CONFIG_SYSCTL=y CONFIG_BINFMT_AOUT=y
CONFIG_BINFMT_ELF=y
# CONFIG_BINFMT_MISC is not set
-# CONFIG_BINFMT_JAVA is not set
CONFIG_BINFMT_EM86=y
# CONFIG_PARPORT is not set
@@ -66,37+65,20 @@ CONFIG_BINFMT_EM86=y # Block devices
#
CONFIG_BLK_DEV_FD=y
-CONFIG_BLK_DEV_IDE=y
+# CONFIG_BLK_DEV_IDE is not set
#
# Please see Documentation/ide.txt for help/info on IDE drives
#
-# CONFIG_BLK_DEV_HD_IDE is not set
-CONFIG_BLK_DEV_IDEDISK=y
-CONFIG_BLK_DEV_IDECD=y
-# CONFIG_BLK_DEV_IDETAPE is not set
-CONFIG_BLK_DEV_IDEFLOPPY=y
-# CONFIG_BLK_DEV_IDESCSI is not set
-CONFIG_BLK_DEV_CMD640=y
-CONFIG_BLK_DEV_CMD640_ENHANCED=y
-# CONFIG_BLK_DEV_RZ1000 is not set
-CONFIG_BLK_DEV_IDEPCI=y
-CONFIG_BLK_DEV_IDEDMA=y
-CONFIG_IDEDMA_AUTO=y
-CONFIG_BLK_DEV_OPTI621=y
-CONFIG_BLK_DEV_TRM290=y
-CONFIG_BLK_DEV_NS87415=y
-CONFIG_BLK_DEV_CMD646=y
-# CONFIG_IDE_CHIPSETS is not set
+# CONFIG_BLK_DEV_HD_ONLY is not set
#
# Additional Block Devices
#
-CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_LOOP is not set
# CONFIG_BLK_DEV_NBD is not set
# CONFIG_BLK_DEV_MD is not set
-CONFIG_BLK_DEV_RAM=y
-CONFIG_BLK_DEV_INITRD=y
+# CONFIG_BLK_DEV_RAM is not set
# CONFIG_BLK_DEV_XD is not set
CONFIG_PARIDE_PARPORT=y
# CONFIG_PARIDE is not set
@@ -105,61+87,34 @@ CONFIG_PARIDE_PARPORT=y #
# Networking options
#
-CONFIG_PACKET=y
+# CONFIG_PACKET is not set
# CONFIG_NETLINK is not set
-CONFIG_FIREWALL=y
-CONFIG_NET_ALIAS=y
+# CONFIG_FIREWALL is not set
+# CONFIG_NET_ALIAS is not set
# CONFIG_FILTER is not set
CONFIG_UNIX=y
CONFIG_INET=y
-CONFIG_IP_MULTICAST=y
+# CONFIG_IP_MULTICAST is not set
# CONFIG_IP_ADVANCED_ROUTER is not set
# CONFIG_IP_PNP is not set
-CONFIG_IP_FIREWALL=y
-CONFIG_IP_TRANSPARENT_PROXY=y
-CONFIG_IP_ALWAYS_DEFRAG=y
-CONFIG_IP_MASQUERADE=y
-
-#
-# Protocol-specific masquerading support will be built as modules.
-#
-CONFIG_IP_MASQUERADE_ICMP=y
-
-#
-# Protocol-specific masquerading support will be built as modules.
-#
-# CONFIG_IP_MASQUERADE_IPAUTOFW is not set
-# CONFIG_IP_MASQUERADE_IPPORTFW is not set
# CONFIG_IP_ROUTER is not set
# CONFIG_NET_IPIP is not set
# CONFIG_NET_IPGRE is not set
-# CONFIG_IP_MROUTE is not set
-CONFIG_IP_ALIAS=y
-CONFIG_SYN_COOKIES=y
+# CONFIG_IP_ALIAS is not set
+# CONFIG_SYN_COOKIES is not set
#
# (it is safe to leave these untouched)
#
-CONFIG_INET_RARP=y
+# CONFIG_INET_RARP is not set
CONFIG_IP_NOSR=y
CONFIG_SKB_LARGE=y
-# CONFIG_IPV6 is not set
#
#
#
# CONFIG_IPX is not set
# CONFIG_ATALK is not set
-# CONFIG_X25 is not set
-# CONFIG_LAPB is not set
-# CONFIG_BRIDGE is not set
-# CONFIG_LLC is not set
-# CONFIG_ECONET is not set
-# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
-# CONFIG_NET_HW_FLOWCONTROL is not set
-# CONFIG_CPU_IS_SLOW is not set
-# CONFIG_NET_SCHED is not set
#
# SCSI support
@@ -170,9+125,9 @@ CONFIG_SCSI=y # SCSI support type (disk, tape, CD-ROM)
#
CONFIG_BLK_DEV_SD=y
-CONFIG_CHR_DEV_ST=y
+# CONFIG_CHR_DEV_ST is not set
CONFIG_BLK_DEV_SR=y
-CONFIG_BLK_DEV_SR_VENDOR=y
+# CONFIG_BLK_DEV_SR_VENDOR is not set
# CONFIG_CHR_DEV_SG is not set
#
@@ -189,15+144,11 @@ CONFIG_SCSI_CONSTANTS=y # CONFIG_SCSI_AHA152X is not set
# CONFIG_SCSI_AHA1542 is not set
# CONFIG_SCSI_AHA1740 is not set
-CONFIG_SCSI_AIC7XXX=y
-# CONFIG_OVERRIDE_CMDS is not set
-# CONFIG_AIC7XXX_PROC_STATS is not set
-CONFIG_AIC7XXX_RESET_DELAY=5
+# CONFIG_SCSI_AIC7XXX is not set
# CONFIG_SCSI_ADVANSYS is not set
# CONFIG_SCSI_IN2000 is not set
# CONFIG_SCSI_AM53C974 is not set
-CONFIG_SCSI_BUSLOGIC=y
-# CONFIG_SCSI_OMIT_FLASHPOINT is not set
+# CONFIG_SCSI_BUSLOGIC is not set
# CONFIG_SCSI_DTC3280 is not set
# CONFIG_SCSI_EATA_DMA is not set
# CONFIG_SCSI_EATA_PIO is not set
@@ -207,13+158,7 @@ CONFIG_SCSI_BUSLOGIC=y # CONFIG_SCSI_GENERIC_NCR5380 is not set
# CONFIG_SCSI_NCR53C406A is not set
# CONFIG_SCSI_NCR53C7xx is not set
-CONFIG_SCSI_NCR53C8XX=y
-CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8
-CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32
-CONFIG_SCSI_NCR53C8XX_SYNC=20
-# CONFIG_SCSI_NCR53C8XX_PROFILE is not set
-# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set
-# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
+# CONFIG_SCSI_NCR53C8XX is not set
# CONFIG_SCSI_PAS16 is not set
# CONFIG_SCSI_PCI2000 is not set
# CONFIG_SCSI_PCI2220I is not set
@@ -225,50+170,41 @@ CONFIG_SCSI_QLOGIC_ISP=y # CONFIG_SCSI_T128 is not set
# CONFIG_SCSI_U14_34F is not set
# CONFIG_SCSI_ULTRASTOR is not set
-# CONFIG_SCSI_DEBUG is not set
#
# Network device support
#
CONFIG_NETDEVICES=y
# CONFIG_ARCNET is not set
-# CONFIG_DUMMY is not set
+CONFIG_DUMMY=m
# CONFIG_EQUALIZER is not set
CONFIG_NET_ETHERNET=y
# CONFIG_NET_VENDOR_3COM is not set
# CONFIG_LANCE is not set
# CONFIG_NET_VENDOR_SMC is not set
# CONFIG_NET_VENDOR_RACAL is not set
-# CONFIG_RTL8139 is not set
-# CONFIG_YELLOWFIN is not set
# CONFIG_NET_ISA is not set
CONFIG_NET_EISA=y
# CONFIG_PCNET32 is not set
-# CONFIG_AC3200 is not set
# CONFIG_APRICOT is not set
# CONFIG_CS89x0 is not set
-# CONFIG_DE4X5 is not set
-CONFIG_DEC_ELCP=y
+CONFIG_DE4X5=y
+# CONFIG_DEC_ELCP is not set
# CONFIG_DGRS is not set
# CONFIG_EEXPRESS_PRO100 is not set
-# CONFIG_LNE390 is not set
# CONFIG_NE2K_PCI is not set
# CONFIG_TLAN is not set
-# CONFIG_ES3210 is not set
-CONFIG_EPIC100=y
-# CONFIG_ZNET is not set
# CONFIG_NET_POCKET is not set
# CONFIG_FDDI is not set
# CONFIG_DLCI is not set
-CONFIG_PPP=y
-
-#
-# CCP compressors for PPP are only built as modules.
-#
+# CONFIG_PPP is not set
# CONFIG_SLIP is not set
# CONFIG_NET_RADIO is not set
# CONFIG_TR is not set
-# CONFIG_SHAPER is not set
+# CONFIG_HOSTESS_SV11 is not set
+# CONFIG_WAN_DRIVERS is not set
+# CONFIG_LAPBETHER is not set
+# CONFIG_X25_ASY is not set
#
# Amateur Radio support
@@ -291,7+227,7 @@ CONFIG_PPP=y CONFIG_VT=y
CONFIG_VT_CONSOLE=y
CONFIG_SERIAL=y
-CONFIG_SERIAL_CONSOLE=y
+# CONFIG_SERIAL_CONSOLE is not set
# CONFIG_SERIAL_EXTENDED is not set
# CONFIG_SERIAL_NONSTANDARD is not set
CONFIG_UNIX98_PTYS=y
@@ -324,14+260,14 @@ CONFIG_PSMOUSE=y # CONFIG_MINIX_FS is not set
CONFIG_EXT2_FS=y
CONFIG_ISO9660_FS=y
-CONFIG_JOLIET=y
-CONFIG_FAT_FS=y
-CONFIG_MSDOS_FS=y
+# CONFIG_JOLIET is not set
+# CONFIG_FAT_FS is not set
+# CONFIG_MSDOS_FS is not set
# CONFIG_UMSDOS_FS is not set
# CONFIG_VFAT_FS is not set
CONFIG_PROC_FS=y
CONFIG_NFS_FS=y
-CONFIG_NFSD=m
+# CONFIG_NFSD is not set
CONFIG_SUNRPC=y
CONFIG_LOCKD=y
# CONFIG_CODA_FS is not set
@@ -342,100+278,25 @@ CONFIG_LOCKD=y # CONFIG_AFFS_FS is not set
# CONFIG_HFS_FS is not set
# CONFIG_ROMFS_FS is not set
-CONFIG_AUTOFS_FS=m
+# CONFIG_AUTOFS_FS is not set
# CONFIG_UFS_FS is not set
CONFIG_DEVPTS_FS=y
-# CONFIG_ADFS_FS is not set
-# CONFIG_QNX4FS_FS is not set
# CONFIG_MAC_PARTITION is not set
-CONFIG_NLS=y
-
-#
-# Native Language Support
-#
-CONFIG_NLS_CODEPAGE_437=y
-# CONFIG_NLS_CODEPAGE_737 is not set
-# CONFIG_NLS_CODEPAGE_775 is not set
-# CONFIG_NLS_CODEPAGE_850 is not set
-# CONFIG_NLS_CODEPAGE_852 is not set
-# CONFIG_NLS_CODEPAGE_855 is not set
-# CONFIG_NLS_CODEPAGE_857 is not set
-# CONFIG_NLS_CODEPAGE_860 is not set
-# CONFIG_NLS_CODEPAGE_861 is not set
-# CONFIG_NLS_CODEPAGE_862 is not set
-# CONFIG_NLS_CODEPAGE_863 is not set
-# CONFIG_NLS_CODEPAGE_864 is not set
-# CONFIG_NLS_CODEPAGE_865 is not set
-# CONFIG_NLS_CODEPAGE_866 is not set
-# CONFIG_NLS_CODEPAGE_869 is not set
-# CONFIG_NLS_CODEPAGE_874 is not set
-# CONFIG_NLS_ISO8859_1 is not set
-# CONFIG_NLS_ISO8859_2 is not set
-# CONFIG_NLS_ISO8859_3 is not set
-# CONFIG_NLS_ISO8859_4 is not set
-# CONFIG_NLS_ISO8859_5 is not set
-# CONFIG_NLS_ISO8859_6 is not set
-# CONFIG_NLS_ISO8859_7 is not set
-# CONFIG_NLS_ISO8859_8 is not set
-# CONFIG_NLS_ISO8859_9 is not set
-# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS is not set
#
# Console drivers
#
CONFIG_VGA_CONSOLE=y
-CONFIG_FB=y
-CONFIG_PCI_CONSOLE=y
-CONFIG_DUMMY_CONSOLE=y
-# CONFIG_FB_VGA is not set
-CONFIG_FB_TGA=y
-# CONFIG_FB_VIRTUAL is not set
-# CONFIG_FBCON_ADVANCED is not set
-CONFIG_FBCON_CFB8=y
-CONFIG_FBCON_CFB32=y
-# CONFIG_FBCON_FONTWIDTH8_ONLY is not set
-# CONFIG_FBCON_FONTS is not set
-CONFIG_FONT_8x8=y
-CONFIG_FONT_8x16=y
+# CONFIG_FB is not set
#
# Sound
#
-CONFIG_SOUND=m
-# CONFIG_SOUND_ES1370 is not set
-# CONFIG_SOUND_ES1371 is not set
-# CONFIG_SOUND_SONICVIBES is not set
-# CONFIG_SOUND_MSNDCLAS is not set
-# CONFIG_SOUND_MSNDPIN is not set
-CONFIG_SOUND_OSS=m
-# CONFIG_SOUND_PAS is not set
-CONFIG_SOUND_SB=m
-# CONFIG_SOUND_ADLIB is not set
-# CONFIG_SOUND_GUS is not set
-# CONFIG_SOUND_MPU401 is not set
-# CONFIG_SOUND_PSS is not set
-CONFIG_SOUND_MSS=m
-# CONFIG_SOUND_SSCAPE is not set
-# CONFIG_SOUND_TRIX is not set
-# CONFIG_SOUND_MAD16 is not set
-# CONFIG_SOUND_WAVEFRONT is not set
-# CONFIG_SOUND_CS4232 is not set
-# CONFIG_SOUND_MAUI is not set
-# CONFIG_SOUND_SGALAXY is not set
-# CONFIG_SOUND_OPL3SA1 is not set
-CONFIG_SOUND_SOFTOSS=m
-CONFIG_SOUND_YM3812=m
-# CONFIG_SOUND_VMIDI is not set
-# CONFIG_SOUND_UART6850 is not set
-
-#
-# Additional low level sound drivers
-#
-# CONFIG_LOWLEVEL_SOUND is not set
+# CONFIG_SOUND is not set
#
# Kernel hacking
#
-# CONFIG_EARLY_SERIAL_CONSOLE is not set
CONFIG_MATHEMU=y
-CONFIG_MAGIC_SYSRQ=y
+# CONFIG_MAGIC_SYSRQ is not set
-#include <linux/config.h>
#include <linux/types.h>
#include <linux/stddef.h>
#include <linux/sched.h>
* kernel entry-points
*/
-#include <linux/config.h>
#include <asm/system.h>
#define halt .long PAL_halt
/* mangled further by Bob Manson (manson@santafe.edu) */
/* more mutilation by David Mosberger (davidm@azstarnet.com) */
-#include <linux/head.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
* Code supporting the RUFFIAN.
*/
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/mm.h>
#undef __EXTERN_INLINE
#include <linux/signal.h>
-#include <linux/head.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/config.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>
* DMA functions specific to EBSA-285/CATS architectures
*/
+#include <linux/config.h>
#include <linux/sched.h>
#include <linux/malloc.h>
#include <linux/mman.h>
/* edited by Linus Torvalds */
/* edited for ARM by Russell King */
-#include <linux/head.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/config.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/signal.h>
#include <linux/sched.h>
-#include <linux/head.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/config.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/signal.h>
#include <linux/sched.h>
-#include <linux/head.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
--- /dev/null
+#ifndef __ARCH_DESC_H
+#define __ARCH_DESC_H
+
+struct desc_struct {
+ unsigned long a,b;
+};
+
+extern struct desc_struct gdt_table[];
+extern struct desc_struct *idt, *gdt;
+
+struct Xgt_desc_struct {
+ unsigned short size;
+ unsigned long address __attribute__((packed));
+};
+
+#define idt_descr (*(struct Xgt_desc_struct *)((char *)&idt - 2))
+#define gdt_descr (*(struct Xgt_desc_struct *)((char *)&gdt - 2))
+
+/*
+ * Entry into gdt where to find first TSS. GDT layout:
+ * 0 - null
+ * 1 - not used
+ * 2 - kernel code segment
+ * 3 - kernel data segment
+ * 4 - user code segment
+ * 5 - user data segment
+ * 6 - not used
+ * 7 - not used
+ * 8 - APM BIOS support
+ * 9 - APM BIOS support
+ * 10 - APM BIOS support
+ * 11 - APM BIOS support
+ * 12 - TSS #0
+ * 13 - LDT #0
+ * 14 - TSS #1
+ * 15 - LDT #1
+ */
+#define FIRST_TSS_ENTRY 12
+#define FIRST_LDT_ENTRY (FIRST_TSS_ENTRY+1)
+#define _TSS(n) ((((unsigned long) n)<<4)+(FIRST_TSS_ENTRY<<3))
+#define _LDT(n) ((((unsigned long) n)<<4)+(FIRST_LDT_ENTRY<<3))
+#define load_TR(n) __asm__ __volatile__("ltr %%ax": /* no output */ :"a" (_TSS(n)))
+#define load_ldt(n) __asm__ __volatile__("lldt %%ax": /* no output */ :"a" (_LDT(n)))
+#define store_TR(n) \
+__asm__("str %%ax\n\t" \
+ "subl %2,%%eax\n\t" \
+ "shrl $4,%%eax" \
+ :"=a" (n) \
+ :"0" (0),"i" (FIRST_TSS_ENTRY<<3))
+
+extern void set_intr_gate(unsigned int irq, void * addr);
+extern void set_ldt_desc(unsigned int n, void *addr, unsigned int size);
+extern void set_tss_desc(unsigned int n, void *addr);
+
+/*
+ * This is the ldt that every process will get unless we need
+ * something other than this.
+ */
+extern struct desc_struct default_ldt;
+
+#endif
@@ -509,7+509,6 @@ ENTRY(empty_bad_page_table) ENTRY(empty_zero_page)
.org 0x6000
-ENTRY(this_must_match_init_task)
/*
* This starts the data section. Note that the above is all
@@ -519,10+518,6 @@ ENTRY(this_must_match_init_task) .data
ALIGN
-/* 256 quadwords - 2048 bytes of idt */
-ENTRY(idt_table)
- .fill 256,8,0 # idt is uninitialized
-
/*
* This contains up to 8192 quadwords depending on NR_TASKS - 64kB of
* gdt entries. Ugh.
#include <asm/uaccess.h>
#include <asm/pgtable.h>
+#include "desc.h"
+
static struct vm_area_struct init_mmap = INIT_MMAP;
static struct fs_struct init_fs = INIT_FS;
static struct file * init_fd_array[NR_OPEN] = { NULL, };
@@ -15,10+17,9 @@ struct mm_struct init_mm = INIT_MM; * Initial task structure.
*
* We need to make sure that this is 8192-byte aligned due to the
- * way process stacks are handled. This is done by making sure
- * the linker maps this in the .text segment right after head.S,
- * and making head.S ensure the proper alignment.
- *
- * The things we do for performance..
+ * way process stacks are handled. This is done by having a special
+ * "init_task" linker map entry..
*/
-union task_union init_task_union __attribute__((__section__(".text"))) = { INIT_TASK };
+union task_union init_task_union
+ __attribute__((__section__(".data.init_task"))) = { INIT_TASK };
+
#include <asm/delay.h>
#include "irq.h"
+#include "desc.h"
unsigned int local_bh_count[NR_CPUS];
unsigned int local_irq_count[NR_CPUS];
#include <asm/system.h>
#include <asm/ldt.h>
+#include "desc.h"
+
static int read_ldt(void * ptr, unsigned long bytecount)
{
void * address = current->mm->segments;
@@ -23,11+25,9 @@ static int read_ldt(void * ptr, unsigned long bytecount)
if (!ptr)
return -EINVAL;
+ if (!address)
+ return 0;
size = LDT_ENTRIES*LDT_ENTRY_SIZE;
- if (!address) {
- address = &default_ldt;
- size = sizeof(default_ldt);
- }
if (size > bytecount)
size = bytecount;
return copy_to_user(ptr, address, size) ? -EFAULT : size;
@@ -81,7+81,7 @@ static int write_ldt(void * ptr, unsigned long bytecount, int oldmode) if (!mm->segments) {
int i = current->tarray_ptr - &task[0];
mm->segments = ldt;
- set_ldt_desc(gdt+(i<<1)+FIRST_LDT_ENTRY, ldt, LDT_ENTRIES);
+ set_ldt_desc(i, ldt, LDT_ENTRIES);
current->tss.ldt = _LDT(i);
load_ldt(i);
if (atomic_read(&mm->count) > 1)
#ifdef CONFIG_MATH_EMULATION
#include <asm/math_emu.h>
#endif
+
#include "irq.h"
+#include "desc.h"
spinlock_t semaphore_wake_lock = SPIN_LOCK_UNLOCKED;
@@ -553,9+555,8 @@ void copy_segments(int nr, struct task_struct *p, struct mm_struct *new_mm) {
struct mm_struct * old_mm = current->mm;
void * old_ldt = old_mm->segments, * ldt = old_ldt;
- int ldt_size = LDT_ENTRIES;
- /* "default_ldt" - use the one from init_task */
+ /* default LDT - use the one from init_task */
p->tss.ldt = _LDT(0);
if (old_ldt) {
if (new_mm) {
@@ -568,7+569,7 @@ void copy_segments(int nr, struct task_struct *p, struct mm_struct *new_mm) memcpy(ldt, old_ldt, LDT_ENTRIES*LDT_ENTRY_SIZE);
}
p->tss.ldt = _LDT(nr);
- set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY, ldt, ldt_size);
+ set_ldt_desc(nr, ldt, LDT_ENTRIES);
return;
}
}
@@ -595,7+596,7 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long esp, p->tss.ss0 = __KERNEL_DS;
p->tss.tr = _TSS(nr);
- set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));
+ set_tss_desc(nr,&(p->tss));
p->tss.eip = (unsigned long) ret_from_fork;
savesegment(fs,p->tss.fs);
/* edited by Linus Torvalds */
#include <linux/config.h> /* for CONFIG_MATH_EMULATION */
-#include <linux/head.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
* state in 'asm.s'.
*/
#include <linux/config.h>
-#include <linux/head.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <asm/atomic.h>
#include <asm/debugreg.h>
+#include "desc.h"
+
asmlinkage int system_call(void);
asmlinkage void lcall7(void);
+
struct desc_struct default_ldt = { 0, 0 };
+/*
+ * The IDT has to be page-aligned to simplify the Pentium
+ * F0 0F bug workaround.. We have a special link segment
+ * for this.
+ */
+struct desc_struct idt_table[256] __attribute__((__section__(".data.idt"))) = { {0, 0}, };
+
static inline void console_verbose(void)
{
extern int console_loglevel;
@@ -467,12+476,11 @@ __initfunc(void trap_init_f00f_bug(void)) * move the IDT into it and write protect this page.
*/
page = (unsigned long) vmalloc(PAGE_SIZE);
- memcpy((void *) page, idt_table, 256*8);
-
pgd = pgd_offset(&init_mm, page);
pmd = pmd_offset(pgd, page);
pte = pte_offset(pmd, page);
- *pte = pte_wrprotect(*pte);
+ free_page(pte_page(*pte));
+ *pte = mk_pte(&idt_table, PAGE_KERNEL_RO);
local_flush_tlb();
/*
@@ -496,25+504,24 @@ __asm__ __volatile__ ("movw %%dx,%%ax\n\t" \ :"ax","dx")
/*
- * WARNING! If we ever start to insert IDT entries
- * into the IDT at run-time, we need to be aware of
- * the F0 0F bug workaround that marks the IDT
- * read-only. It should be easy enough to just follow
- * the page tables and set the gate directly..
+ * This needs to use 'idt_table' rather than 'idt', and
+ * thus use the _nonmapped_ version of the IDT, as the
+ * Pentium F0 0F bugfix can have resulted in the mapped
+ * IDT being write-protected.
*/
void set_intr_gate(unsigned int n, void *addr)
{
- _set_gate(idt+(n),14,0,addr);
+ _set_gate(idt_table+n,14,0,addr);
}
static void __init set_trap_gate(unsigned int n, void *addr)
{
- _set_gate(idt+(n),15,0,addr);
+ _set_gate(idt_table+n,15,0,addr);
}
static void __init set_system_gate(unsigned int n, void *addr)
{
- _set_gate(idt+(n),15,3,addr);
+ _set_gate(idt_table+n,15,3,addr);
}
static void __init set_call_gate(void *a, void *addr)
@@ -543,20+550,19 @@ __asm__ __volatile__ ("movw %3,0(%2)\n\t" \ "rorl $16,%%eax" \
: "=m"(*(n)) : "a" (addr), "r"(n), "ir"(limit), "i"(type))
-void set_tss_desc(void *n, void *addr)
+void set_tss_desc(unsigned int n, void *addr)
{
- _set_tssldt_desc(((char *) n),((int)addr), 235, 0x89);
+ _set_tssldt_desc(gdt_table+FIRST_TSS_ENTRY+(n<<1), (int)addr, 235, 0x89);
}
-void set_ldt_desc(void *n, void *addr, unsigned int size)
+void set_ldt_desc(unsigned int n, void *addr, unsigned int size)
{
- _set_tssldt_desc(((char *) n), ((int)addr), ((size << 3) - 1), 0x82);
+ _set_tssldt_desc(gdt_table+FIRST_LDT_ENTRY+(n<<1), (int)addr, ((size << 3) - 1), 0x82);
}
void __init trap_init(void)
{
int i;
- struct desc_struct * p;
if (readl(0x0FFFD9) == 'E' + ('I'<<8) + ('S'<<16) + ('A'<<24))
EISA_bus = 1;
@@ -582,19+588,12 @@ void __init trap_init(void) for (i=18;i<48;i++)
set_trap_gate(i,&reserved);
set_system_gate(0x80,&system_call);
-/* set up GDT task & ldt entries */
- p = gdt+FIRST_TSS_ENTRY;
- set_tss_desc(p, &init_task.tss);
- p++;
- set_ldt_desc(p, &default_ldt, 1);
- p++;
- for(i=1 ; i<NR_TASKS ; i++) {
- p->a=p->b=0;
- p++;
- p->a=p->b=0;
- p++;
- }
-/* Clear NT, so that we won't have troubles with that later on */
+
+ /* set up GDT task & ldt entries */
+ set_tss_desc(0, &init_task.tss);
+ set_ldt_desc(0, &default_ldt, 1);
+
+ /* Clear NT, so that we won't have troubles with that later on */
__asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl");
load_TR(0);
load_ldt(0);
#include <linux/stddef.h>
-#include <linux/head.h>
#include <asm/uaccess.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>
@@ -76,7+75,8 @@ bad_area: return 0;
}
-asmlinkage void do_invalid_op (struct pt_regs *, unsigned long);
+asmlinkage void do_invalid_op(struct pt_regs *, unsigned long);
+extern unsigned long idt;
/*
* This routine handles page faults. It determines the address,
@@ -186,7+186,7 @@ bad_area: if (boot_cpu_data.f00f_bug) {
unsigned long nr;
- nr = (address - (unsigned long) idt) >> 3;
+ nr = (address - idt) >> 3;
if (nr == 6) {
do_invalid_op(regs, 0);
#include <linux/config.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>
@@ -35,6+35,9 @@ SECTIONS
_edata = .; /* End of data section */
+ . = ALIGN(8192); /* init_task */
+ .data.init_task : { *(.data.init_task) }
+
. = ALIGN(4096); /* Init code and data */
__init_begin = .;
.text.init : { *(.text.init) }
@@ -42,6+45,9 @@ SECTIONS . = ALIGN(4096);
__init_end = .;
+ . = ALIGN(4096);
+ .data.page_aligned : { *(.data.idt) }
+
__bss_start = .; /* BSS */
.bss : {
*(.bss)
/* edited by Linus Torvalds */
/* further hacked for MIPS by David S. Miller (dm@engr.sgi.com) */
-#include <linux/head.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.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/init.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 <stddef.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/config.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/config.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/mm.h>
#include <linux/malloc.h>
#include <linux/sched.h>
-#include <linux/head.h>
#include <linux/kernel.h>
#include <linux/kernel_stat.h>
#include <linux/errno.h>
#include <linux/config.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/mm.h>
#include <linux/malloc.h>
#include <linux/sched.h>
-#include <linux/head.h>
#include <linux/kernel.h>
#include <linux/kernel_stat.h>
#include <linux/errno.h>
*/
static char *version =
-"3c59x.c:v0.99F 8/7/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/vortex.html\n";
+"3c59x.c:v0.99E 5/12/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/vortex.html\n";
/* "Knobs" that adjust features and parameters. */
/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
Setting to > 1512 effectively disables this feature. */
-static const int rx_copybreak = 200;
+static const rx_copybreak = 200;
/* Allow setting MTU to a larger size, bypassing the normal ethernet setup. */
-static const int mtu = 1500;
+static const mtu = 1500;
/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
static int max_interrupt_work = 20;
@@ -37,6+37,9 @@ static int vortex_debug = 1; debugging. */
static int rx_nocopy = 0, rx_copy = 0, queued_packet = 0, rx_csumhits;
+/* Enable the automatic media selection code -- usually set. */
+#define AUTOMEDIA 1
+
/* Allow the use of fragment bus master transfers instead of only
programmed-I/O for Vortex cards. Full-bus-master transfers are always
enabled by default on Boomerang cards. If VORTEX_BUS_MASTER is defined,
@@ -74,9+77,7 @@ static int rx_nocopy = 0, rx_copy = 0, queued_packet = 0, rx_csumhits; #include <linux/malloc.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#if LINUX_VERSION_CODE < 0x20155 || defined(CARDBUS)
#include <linux/bios32.h>
-#endif
#include <linux/timer.h>
#include <asm/irq.h> /* For NR_IRQS only. */
#include <asm/bitops.h>
@@ -104,6+105,11 @@ static int rx_nocopy = 0, rx_copy = 0, queued_packet = 0, rx_csumhits; #define RUN_AT(x) (jiffies + (x))
#define DEV_ALLOC_SKB(len) dev_alloc_skb(len)
#endif
+#if LINUX_VERSION_CODE < 0x20159
+#define DEV_FREE_SKB(skb) dev_kfree_skb (skb, FREE_WRITE);
+#else /* Grrr, unneeded incompatible change. */
+#define DEV_FREE_SKB(skb) dev_kfree_skb(skb);
+#endif
#ifdef SA_SHIRQ
#define FREE_IRQ(irqnum, dev) free_irq(irqnum, dev)
@@ -122,27+128,9 @@ static int rx_nocopy = 0, rx_copy = 0, queued_packet = 0, rx_csumhits; #define udelay(microsec) do { int _i = 4*microsec; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
#endif
-#if LINUX_VERSION_CODE <= 0x20139
-#define net_device_stats enet_statistics
-#define NETSTATS_VER2
-#endif
#if LINUX_VERSION_CODE < 0x20138
#define test_and_set_bit(val, addr) set_bit(val, addr)
-#define le32_to_cpu(val) (val)
-#define cpu_to_le32(val) (val)
#endif
-#if LINUX_VERSION_CODE < 0x20155
-#define PCI_SUPPORT_VER1
-#else
-#define PCI_SUPPORT_VER2
-#endif
-#if LINUX_VERSION_CODE < 0x20159
-#define DEV_FREE_SKB(skb) dev_kfree_skb (skb, FREE_WRITE);
-#else /* Grrr, unneeded incompatible change. */
-#define DEV_FREE_SKB(skb) dev_kfree_skb(skb);
-#endif
-
-
#if defined(MODULE) && (LINUX_VERSION_CODE >= 0x20115)
MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
MODULE_DESCRIPTION("3Com 3c590/3c900 series Vortex/Boomerang driver");
@@ -153,7+141,7 @@ MODULE_PARM(rx_copybreak, "i"); MODULE_PARM(max_interrupt_work, "i");
MODULE_PARM(compaq_ioaddr, "i");
MODULE_PARM(compaq_irq, "i");
-MODULE_PARM(compaq_device_id, "i");
+MODULE_PARM(compaq_prod_id, "i");
#endif
/* Operational parameter that usually are not changed. */
@@ -178,7+166,7 @@ static char mii_preamble_required = 0; /* Caution! These entries must be consistent. */
static const int product_ids[] = {
0x5900, 0x5920, 0x5970, 0x5950, 0x5951, 0x5952, 0x9000, 0x9001,
- 0x9050, 0x9051, 0x9055, 0x5057, 0x5175, 0 };
+ 0x9050, 0x9051, 0x9055, 0x5057, 0 };
static const char *product_names[] = {
"3c590 Vortex 10Mbps",
"3c592 EISA 10mbps Demon/Vortex",
@@ -192,7+180,6 @@ static const char *product_names[] = { "3c905 Boomerang 100baseT4",
"3c905B Cyclone 100baseTx",
"3c575", /* Cardbus Boomerang */
- "3CCFE575", /* Cardbus ?Cyclone? */
};
/*
@@ -205,16+192,17 @@ XL, 3Com's PCI to 10/100baseT adapters. It also works with the 10Mbs versions of the FastEtherLink cards. The supported product IDs are
3c590, 3c592, 3c595, 3c597, 3c900, 3c905
-The related ISA 3c515 is supported with a separate driver, 3c515.c, included
-with the kernel source or available from
+The ISA 3c515 is supported with a seperate driver, 3c515.c, included with
+the kernel source or available from
cesdis.gsfc.nasa.gov:/pub/linux/drivers/3c515.html
II. Board-specific settings
PCI bus devices are configured by the system at boot time, so no jumpers
need to be set on the board. The system BIOS should be set to assign the
-PCI INTA signal to an otherwise unused system IRQ line. Note: The 1.2.*
-kernels did not support PCI interrupt sharing.
+PCI INTA signal to an otherwise unused system IRQ line. While it's
+physically possible to shared PCI interrupt lines, the 1.2.0 kernel doesn't
+support it.
III. Driver operation
@@ -222,10+210,10 @@ The 3c59x series use an interface that's very similar to the previous 3c5x9 series. The primary interface is two programmed-I/O FIFOs, with an
alternate single-contiguous-region bus-master transfer (see next).
-The 3c900 "Boomerang" series uses a full-bus-master interface with separate
+The 3c900 "Boomerang" series uses a full-bus-master interface with seperate
lists of transmit and receive descriptors, similar to the AMD LANCE/PCnet,
DEC Tulip and Intel Speedo3. The first chip version retains a compatible
-programmed-I/O interface that has been removed in 'B' and subsequent board
+programmed-I/O interface that will be removed in the 'B' and subsequent
revisions.
One extension that is advertised in a very large font is that the adapters
@@ -243,7+231,7 @@ packets may be reordered and receive buffer groups are associated with a single frame.
With full-bus-master support, this driver uses a "RX_COPYBREAK" scheme.
-Rather than a fixed intermediate receive buffer, this scheme allocates
+Tather than a fixed intermediate receive buffer, this scheme allocates
full-sized skbuffs as receive buffers. The value RX_COPYBREAK is used as
the copying breakpoint: it is chosen to trade-off the memory wasted by
passing the full-sized skbuff to the queue layer for all frames vs. the
@@ -416,7+404,7 @@ struct vortex_private { struct sk_buff* tx_skbuff[TX_RING_SIZE];
unsigned int cur_rx, cur_tx; /* The next free ring entry */
unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
- struct net_device_stats stats;
+ struct enet_statistics stats;
struct sk_buff *tx_skb; /* Packet being eaten by bus master ctrl. */
/* PCI configuration space information. */
@@ -424,16+412,17 @@ struct vortex_private { u16 pci_device_id;
/* The remainder are related to chip state, mostly media selection. */
- unsigned long in_interrupt;
+ int in_interrupt;
struct timer_list timer; /* Media selection timer. */
int options; /* User-settable misc. driver options. */
- unsigned int media_override:3, /* Passed-in media type. */
- default_media:4, /* Read from the EEPROM/Wn3_Config. */
- full_duplex:1, force_fd:1, autoselect:1,
- bus_master:1, /* Vortex can only do a fragment bus-m. */
- full_bus_master_tx:1, full_bus_master_rx:2, /* Boomerang */
- hw_csums:1, /* Has hardware checksums. */
- tx_full:1;
+ unsigned int
+ media_override:3, /* Passed-in media type. */
+ default_media:3, /* Read from the EEPROM/Wn3_Config. */
+ full_duplex:1, autoselect:1,
+ bus_master:1, /* Vortex can only do a fragment bus-m. */
+ full_bus_master_tx:1, full_bus_master_rx:2, /* Boomerang */
+ hw_csums:1, /* Has hardware checksums. */
+ tx_full:1;
u16 status_enable;
u16 available_media; /* From Wn3_Options. */
u16 capabilities, info1, info2; /* Various, from EEPROM. */
@@ -470,15+459,15 @@ static struct media_table { };
static int vortex_scan(struct device *dev);
-static struct device *vortex_found_device(struct device *dev, long ioaddr,
+static struct device *vortex_found_device(struct device *dev, int ioaddr,
int irq, int device_id,
int options, int card_idx);
static int vortex_probe1(struct device *dev);
static int vortex_open(struct device *dev);
-static void mdio_sync(long ioaddr, int bits);
-static int mdio_read(long ioaddr, int phy_id, int location);
+static void mdio_sync(int ioaddr, int bits);
+static int mdio_read(int ioaddr, int phy_id, int location);
#ifdef HAVE_PRIVATE_IOCTL
-static void mdio_write(long ioaddr, int phy_id, int location, int value);
+static void mdio_write(int ioaddr, int phy_id, int location, int value);
#endif
static void vortex_timer(unsigned long arg);
static int vortex_start_xmit(struct sk_buff *skb, struct device *dev);
@@ -487,8+476,8 @@ static int vortex_rx(struct device *dev); static int boomerang_rx(struct device *dev);
static void vortex_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs);
static int vortex_close(struct device *dev);
-static void update_stats(long ioaddr, struct device *dev);
-static struct net_device_stats *vortex_get_stats(struct device *dev);
+static void update_stats(int addr, struct device *dev);
+static struct enet_statistics *vortex_get_stats(struct device *dev);
static void set_rx_mode(struct device *dev);
#ifdef HAVE_PRIVATE_IOCTL
static int vortex_ioctl(struct device *dev, struct ifreq *rq, int cmd);
@@ -671,24+660,6 @@ static int vortex_scan(struct device *dev) if (vendor != TCOM_VENDOR_ID)
continue;
- /* Power-up the card. */
- pcibios_read_config_word(pci_bus, pci_device_fn,
- 0xe0, &pci_command);
- if (pci_command & 0x3) {
- /* Save the ioaddr and IRQ info! */
- printk(KERN_INFO " A 3Com network adapter is powered down!"
- " Setting the power state %4.4x->%4.4x.\n",
- pci_command, pci_command & ~3);
- pcibios_write_config_word(pci_bus, pci_device_fn,
- 0xe0, pci_command & ~3);
- printk(KERN_INFO " Setting the IRQ to %d, IOADDR to %#lx.\n",
- irq, ioaddr);
- pcibios_write_config_byte(pci_bus, pci_device_fn,
- PCI_INTERRUPT_LINE, irq);
- pcibios_write_config_dword(pci_bus, pci_device_fn,
- PCI_BASE_ADDRESS_0, ioaddr);
- }
-
if (ioaddr == 0) {
printk(KERN_WARNING " A 3Com network adapter has been found, "
"however it has not been assigned an I/O address.\n"
@@ -744,7+715,7 @@ static int vortex_scan(struct device *dev)
/* Now check all slots of the EISA bus. */
if (EISA_bus) {
- static long ioaddr = 0x1000;
+ static int ioaddr = 0x1000;
for ( ; ioaddr < 0x9000; ioaddr += 0x1000) {
int device_id;
if (check_region(ioaddr, VORTEX_TOTAL_SIZE))
@@ -782,7+753,7 @@ static int vortex_scan(struct device *dev) }
static struct device *
-vortex_found_device(struct device *dev, long ioaddr, int irq,
+vortex_found_device(struct device *dev, int ioaddr, int irq,
int device_id, int option, int card_idx)
{
struct vortex_private *vp;
@@ -868,7+839,6 @@ vortex_found_device(struct device *dev, long ioaddr, int irq, vp->full_duplex = 0;
vp->bus_master = 0;
}
- vp->force_fd = vp->full_duplex;
vortex_probe1(dev);
#endif /* MODULE */
@@ -877,13+847,13 @@ vortex_found_device(struct device *dev, long ioaddr, int irq,
static int vortex_probe1(struct device *dev)
{
- long ioaddr = dev->base_addr;
+ int ioaddr = dev->base_addr;
struct vortex_private *vp = (struct vortex_private *)dev->priv;
u16 *ether_addr = (u16 *)dev->dev_addr;
unsigned int eeprom[0x40], checksum = 0; /* EEPROM contents */
int i;
- printk(KERN_INFO "%s: 3Com %s at %#3lx,",
+ printk(KERN_INFO "%s: 3Com %s at %#3x,",
dev->name, vp->product_name, ioaddr);
/* Read the station address from the EEPROM. */
@@ -918,15+888,11 @@ static int vortex_probe1(struct device *dev) ether_addr[i] = htons(eeprom[i + 10]);
for (i = 0; i < 6; i++)
printk("%c%2.2x", i ? ':' : ' ', dev->dev_addr[i]);
-#ifdef __sparc__
- printk(", IRQ %s\n", __irq_itoa(dev->irq));
-#else
printk(", IRQ %d\n", dev->irq);
/* Tell them about an invalid IRQ. */
if (vortex_debug && (dev->irq <= 0 || dev->irq >= NR_IRQS))
printk(KERN_WARNING " *** Warning: IRQ %d is unlikely to work! ***\n",
dev->irq);
-#endif
/* Extract our information from the EEPROM data. */
vp->info1 = eeprom[13];
@@ -952,7+918,7 @@ static int vortex_probe1(struct device *dev) config.u.ram_width ? "word" : "byte",
ram_split[config.u.ram_split],
config.u.autoselect ? "autoselect/" : "",
- config.u.xcvr > XCVR_ExtMII ? "<invalid transceiver>" :
+ config.u.xcvr ? "NWay Autonegotiation" :
media_tbl[config.u.xcvr].name);
vp->default_media = config.u.xcvr;
vp->autoselect = config.u.autoselect;
@@ -965,7+931,7 @@ static int vortex_probe1(struct device *dev) } else
dev->if_port = vp->default_media;
- if (dev->if_port == XCVR_MII || dev->if_port == XCVR_NWAY) {
+ if (dev->if_port == XCVR_MII) {
int phy, phy_idx = 0;
EL3WINDOW(4);
for (phy = 0; phy < 32 && phy_idx < sizeof(vp->phys); phy++) {
@@ -1025,7+991,7 @@ static int vortex_probe1(struct device *dev) static int
vortex_open(struct device *dev)
{
- long ioaddr = dev->base_addr;
+ int ioaddr = dev->base_addr;
struct vortex_private *vp = (struct vortex_private *)dev->priv;
union wn3_config config;
int i;
@@ -1045,8+1011,6 @@ vortex_open(struct device *dev) dev->if_port = XCVR_100baseTx;
while (! (vp->available_media & media_tbl[dev->if_port].mask))
dev->if_port = media_tbl[dev->if_port].next;
- if (vp->phys[0])
- dev->if_port = XCVR_NWAY;
if (vortex_debug > 1)
printk(KERN_DEBUG "%s: Initial media type %s.\n",
@@ -1055,16+1019,15 @@ vortex_open(struct device *dev) init_timer(&vp->timer);
vp->timer.expires = RUN_AT(media_tbl[dev->if_port].wait);
vp->timer.data = (unsigned long)dev;
- vp->timer.function = &vortex_timer; /* timer handler */
+ vp->timer.function = &vortex_timer; /* timer handler */
add_timer(&vp->timer);
} else
dev->if_port = vp->default_media;
- vp->full_duplex = vp->force_fd;
config.u.xcvr = dev->if_port;
outl(config.i, ioaddr + Wn3_Config);
- if (dev->if_port == XCVR_MII || dev->if_port == XCVR_NWAY) {
+ if (dev->if_port == XCVR_MII) {
int mii_reg1, mii_reg5;
EL3WINDOW(4);
/* Read BMSR (reg1) only to clear old status. */
@@ -1164,9+1127,9 @@ vortex_open(struct device *dev) printk(KERN_DEBUG "%s: Filling in the Rx ring.\n", dev->name);
for (i = 0; i < RX_RING_SIZE; i++) {
struct sk_buff *skb;
- vp->rx_ring[i].next = cpu_to_le32(virt_to_bus(&vp->rx_ring[i+1]));
+ vp->rx_ring[i].next = virt_to_bus(&vp->rx_ring[i+1]);
vp->rx_ring[i].status = 0; /* Clear complete bit. */
- vp->rx_ring[i].length = cpu_to_le32(PKT_BUF_SZ | LAST_FRAG);
+ vp->rx_ring[i].length = PKT_BUF_SZ | LAST_FRAG;
skb = DEV_ALLOC_SKB(PKT_BUF_SZ);
vp->rx_skbuff[i] = skb;
if (skb == NULL)
@@ -1174,13+1137,12 @@ vortex_open(struct device *dev) skb->dev = dev; /* Mark as being used by this device. */
#if LINUX_VERSION_CODE >= 0x10300
skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
- vp->rx_ring[i].addr = cpu_to_le32(virt_to_bus(skb->tail));
+ vp->rx_ring[i].addr = virt_to_bus(skb->tail);
#else
vp->rx_ring[i].addr = virt_to_bus(skb->data);
#endif
}
- /* Wrap the ring. */
- vp->rx_ring[i-1].next = cpu_to_le32(virt_to_bus(&vp->rx_ring[0]));
+ vp->rx_ring[i-1].next = virt_to_bus(&vp->rx_ring[0]); /* Wrap the ring. */
outl(virt_to_bus(&vp->rx_ring[0]), ioaddr + UpListPtr);
}
if (vp->full_bus_master_tx) { /* Boomerang bus master Tx. */
@@ -1224,25+1186,24 @@ vortex_open(struct device *dev)
static void vortex_timer(unsigned long data)
{
+#ifdef AUTOMEDIA
struct device *dev = (struct device *)data;
struct vortex_private *vp = (struct vortex_private *)dev->priv;
- long ioaddr = dev->base_addr;
+ int ioaddr = dev->base_addr;
unsigned long flags;
- int next_tick = 0;
int ok = 0;
- int media_status, old_window;
if (vortex_debug > 1)
printk(KERN_DEBUG "%s: Media selection timer tick happened, %s.\n",
dev->name, media_tbl[dev->if_port].name);
- save_flags(flags);
- cli();
- old_window = inw(ioaddr + EL3_CMD) >> 13;
- EL3WINDOW(4);
- media_status = inw(ioaddr + Wn4_Media);
- switch (dev->if_port) {
- case XCVR_10baseT: case XCVR_100baseTx: case XCVR_100baseFx:
+ save_flags(flags); cli(); {
+ int old_window = inw(ioaddr + EL3_CMD) >> 13;
+ int media_status;
+ EL3WINDOW(4);
+ media_status = inw(ioaddr + Wn4_Media);
+ switch (dev->if_port) {
+ case XCVR_10baseT: case XCVR_100baseTx: case XCVR_100baseFx:
if (media_status & Media_LnkBeat) {
ok = 1;
if (vortex_debug > 1)
@@ -1251,27+1212,18 @@ static void vortex_timer(unsigned long data) } else if (vortex_debug > 1)
printk(KERN_DEBUG "%s: Media %s is has no link beat, %x.\n",
dev->name, media_tbl[dev->if_port].name, media_status);
+
break;
- case XCVR_MII: case XCVR_NWAY:
- if (mdio_read(ioaddr, vp->phys[0], 1) & 0x0004) {
+ case XCVR_MII:
+ {
+ int mii_reg1 = mdio_read(ioaddr, vp->phys[0], 1);
int mii_reg5 = mdio_read(ioaddr, vp->phys[0], 5);
- ok = 1;
- if (! vp->force_fd && mii_reg5 != 0xffff) {
- int duplex = (mii_reg5&0x0100) ||
- (mii_reg5 & 0x01C0) == 0x0040;
- if (vp->full_duplex != duplex) {
- vp->full_duplex = duplex;
- printk(KERN_INFO "%s: Setting %s-duplex based on MII "
- "#%d link partner capability of %4.4x.\n",
- dev->name, vp->full_duplex ? "full" : "half",
- vp->phys[0], mii_reg5);
- /* Set the full-duplex bit. */
- outb((vp->full_duplex ? 0x20 : 0) |
- (dev->mtu > 1500 ? 0x40 : 0),
- ioaddr + Wn3_MAC_Ctrl);
- }
- next_tick = 60*HZ;
- }
+ if (vortex_debug > 1)
+ printk(KERN_DEBUG "%s: MII #%d status register is %4.4x, "
+ "link partner capability %4.4x.\n",
+ dev->name, vp->phys[0], mii_reg1, mii_reg5);
+ if (mii_reg1 & 0x0004)
+ ok = 1;
break;
}
default: /* Other media types handled by Tx timeouts. */
@@ -1279,8+1231,8 @@ static void vortex_timer(unsigned long data) printk(KERN_DEBUG "%s: Media %s is has no indication, %x.\n",
dev->name, media_tbl[dev->if_port].name, media_status);
ok = 1;
- }
- if ( ! ok) {
+ }
+ if ( ! ok) {
union wn3_config config;
do {
@@ -1297,7+1249,8 @@ static void vortex_timer(unsigned long data) printk(KERN_DEBUG "%s: Media selection failed, now trying "
"%s port.\n",
dev->name, media_tbl[dev->if_port].name);
- next_tick = RUN_AT(media_tbl[dev->if_port].wait);
+ vp->timer.expires = RUN_AT(media_tbl[dev->if_port].wait);
+ add_timer(&vp->timer);
}
outw((media_status & ~(Media_10TP|Media_SQE)) |
media_tbl[dev->if_port].media_bits, ioaddr + Wn4_Media);
@@ -1309,25+1262,21 @@ static void vortex_timer(unsigned long data)
outw(dev->if_port == XCVR_10base2 ? StartCoax : StopCoax,
ioaddr + EL3_CMD);
- }
- EL3WINDOW(old_window);
- restore_flags(flags);
-
+ }
+ EL3WINDOW(old_window);
+ } restore_flags(flags);
if (vortex_debug > 1)
printk(KERN_DEBUG "%s: Media selection timer finished, %s.\n",
dev->name, media_tbl[dev->if_port].name);
- if (next_tick) {
- vp->timer.expires = RUN_AT(next_tick);
- add_timer(&vp->timer);
- }
+#endif /* AUTOMEDIA*/
return;
}
static void vortex_tx_timeout(struct device *dev)
{
struct vortex_private *vp = (struct vortex_private *)dev->priv;
- long ioaddr = dev->base_addr;
+ int ioaddr = dev->base_addr;
int j;
printk(KERN_ERR "%s: transmit timed out, tx_status %2.2x status %4.4x.\n",
@@ -1360,8+1309,8 @@ static void vortex_tx_timeout(struct device *dev) for (i = 0; i < TX_RING_SIZE; i++) {
printk(KERN_DEBUG " %d: @%p length %8.8x status %8.8x\n", i,
&vp->tx_ring[i],
- le32_to_cpu(vp->tx_ring[i].length),
- le32_to_cpu(vp->tx_ring[i].status));
+ vp->tx_ring[i].length,
+ vp->tx_ring[i].status);
}
}
#endif
@@ -1391,14+1340,14 @@ static void vortex_tx_timeout(struct device *dev) }
/*
- * Handle uncommon interrupt sources. This is a separate routine to minimize
+ * Handle uncommon interrupt sources. This is a seperate routine to minimize
* the cache impact.
*/
static void
vortex_error(struct device *dev, int status)
{
struct vortex_private *vp = (struct vortex_private *)dev->priv;
- long ioaddr = dev->base_addr;
+ int ioaddr = dev->base_addr;
int do_tx_reset = 0;
int i;
@@ -1485,7+1434,7 @@ static int vortex_start_xmit(struct sk_buff *skb, struct device *dev)
{
struct vortex_private *vp = (struct vortex_private *)dev->priv;
- long ioaddr = dev->base_addr;
+ int ioaddr = dev->base_addr;
if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
if (jiffies - dev->trans_start >= TX_TIMEOUT)
@@ -1557,7+1506,7 @@ static int boomerang_start_xmit(struct sk_buff *skb, struct device *dev)
{
struct vortex_private *vp = (struct vortex_private *)dev->priv;
- long ioaddr = dev->base_addr;
+ int ioaddr = dev->base_addr;
if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
if (jiffies - dev->trans_start >= TX_TIMEOUT)
@@ -1579,13+1528,13 @@ boomerang_start_xmit(struct sk_buff *skb, struct device *dev) printk(KERN_WARNING "%s: Tx Ring full, refusing to send buffer.\n",
dev->name);
return 1;
- }
+ }
/* end change 06/25/97 M. Sievers */
vp->tx_skbuff[entry] = skb;
vp->tx_ring[entry].next = 0;
- vp->tx_ring[entry].addr = cpu_to_le32(virt_to_bus(skb->data));
- vp->tx_ring[entry].length = cpu_to_le32(skb->len | LAST_FRAG);
- vp->tx_ring[entry].status = cpu_to_le32(skb->len | TxIntrUploaded);
+ vp->tx_ring[entry].addr = virt_to_bus(skb->data);
+ vp->tx_ring[entry].length = skb->len | LAST_FRAG;
+ vp->tx_ring[entry].status = skb->len | TxIntrUploaded;
save_flags(flags);
cli();
@@ -1594,7+1543,7 @@ boomerang_start_xmit(struct sk_buff *skb, struct device *dev) for (i = 600; i >= 0 ; i--)
if ( (inw(ioaddr + EL3_STATUS) & CmdInProgress) == 0)
break;
- prev_entry->next = cpu_to_le32(virt_to_bus(&vp->tx_ring[entry]));
+ prev_entry->next = virt_to_bus(&vp->tx_ring[entry]);
if (inl(ioaddr + DownListPtr) == 0) {
outl(virt_to_bus(&vp->tx_ring[entry]), ioaddr + DownListPtr);
queued_packet++;
@@ -1606,7+1555,7 @@ boomerang_start_xmit(struct sk_buff *skb, struct device *dev) if (vp->cur_tx - vp->dirty_tx > TX_RING_SIZE - 1)
vp->tx_full = 1;
else { /* Clear previous interrupt enable. */
- prev_entry->status &= cpu_to_le32(~TxIntrUploaded);
+ prev_entry->status &= ~TxIntrUploaded;
clear_bit(0, (void*)&dev->tbusy);
}
dev->trans_start = jiffies;
@@ -1624,8+1573,8 @@ static void vortex_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs) struct device *dev = (struct device *)(irq2dev_map[irq]);
#endif
struct vortex_private *vp;
- long ioaddr;
- int latency, status;
+ int ioaddr, status;
+ int latency;
int work_done = max_interrupt_work;
vp = (struct vortex_private *)dev->priv;
@@ -1637,6+1586,7 @@ static void vortex_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs) dev->interrupt = 1;
ioaddr = dev->base_addr;
latency = inb(ioaddr + Timer);
+
status = inw(ioaddr + EL3_STATUS);
if (vortex_debug > 4)
@@ -1731,7+1681,7 @@ static int vortex_rx(struct device *dev)
{
struct vortex_private *vp = (struct vortex_private *)dev->priv;
- long ioaddr = dev->base_addr;
+ int ioaddr = dev->base_addr;
int i;
short rx_status;
@@ -1801,7+1751,7 @@ boomerang_rx(struct device *dev) {
struct vortex_private *vp = (struct vortex_private *)dev->priv;
int entry = vp->cur_rx % RX_RING_SIZE;
- long ioaddr = dev->base_addr;
+ int ioaddr = dev->base_addr;
int rx_status;
int rx_work_limit = vp->dirty_rx + RX_RING_SIZE - vp->cur_rx;
@@ -1809,7+1759,8 @@ boomerang_rx(struct device *dev) printk(KERN_DEBUG " In boomerang_rx(), status %4.4x, rx_status "
"%4.4x.\n",
inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus));
- while ((rx_status = le32_to_cpu(vp->rx_ring[entry].status)) & RxDComplete) {
+ while ((--rx_work_limit >= 0) &&
+ ((rx_status = vp->rx_ring[entry].status) & RxDComplete)) {
if (rx_status & RxDError) { /* Error, update stats. */
unsigned char rx_error = rx_status >> 16;
if (vortex_debug > 2)
@@ -1838,18+1789,21 @@ boomerang_rx(struct device *dev) skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
/* 'skb_put()' points to the start of sk_buff data area. */
memcpy(skb_put(skb, pkt_len),
- bus_to_virt(le32_to_cpu(vp->rx_ring[entry].addr)),
+ bus_to_virt(vp->rx_ring[entry].addr),
pkt_len);
#else
- memcpy(skb->data, bus_to_virt(vp->rx_ring[entry].addr),
- pkt_len);
+ memcpy(skb->data, bus_to_virt(vp->rx_ring[entry].addr), pkt_len);
skb->len = pkt_len;
#endif
rx_copy++;
- } else {
+ } else{
void *temp;
/* Pass up the skbuff already on the Rx ring. */
skb = vp->rx_skbuff[entry];
+ if (skb == NULL) {
+ printk(KERN_WARNING "%s: in boomerang_rx -- attempt to use NULL skb caught\n", dev->name);
+ break;
+ }
vp->rx_skbuff[entry] = NULL;
#if LINUX_VERSION_CODE >= 0x10300
temp = skb_put(skb, pkt_len);
@@ -1857,11+1811,10 @@ boomerang_rx(struct device *dev) temp = skb->data;
#endif
/* Remove this checking code for final release. */
- if (bus_to_virt(le32_to_cpu(vp->rx_ring[entry].addr)) != temp)
+ if (bus_to_virt(vp->rx_ring[entry].addr) != temp)
printk(KERN_ERR "%s: Warning -- the skbuff addresses do not match"
" in boomerang_rx: %p vs. %p.\n", dev->name,
- bus_to_virt(le32_to_cpu(vp->rx_ring[entry].addr)),
- temp);
+ bus_to_virt(vp->rx_ring[entry].addr), temp);
rx_nocopy++;
}
#if LINUX_VERSION_CODE > 0x10300
@@ -1883,8+1836,6 @@ boomerang_rx(struct device *dev) vp->stats.rx_packets++;
}
entry = (++vp->cur_rx) % RX_RING_SIZE;
- if (--rx_work_limit < 0)
- break;
}
/* Refill the Rx ring buffers. */
for (; vp->dirty_rx < vp->cur_rx; vp->dirty_rx++) {
@@ -1892,12+1843,14 @@ boomerang_rx(struct device *dev) entry = vp->dirty_rx % RX_RING_SIZE;
if (vp->rx_skbuff[entry] == NULL) {
skb = DEV_ALLOC_SKB(PKT_BUF_SZ);
- if (skb == NULL)
+ if (skb == NULL) {
+ printk(KERN_DEBUG "%s: in boomerang_rx -- could not allocate skbuff\n", dev->name);
break; /* Bad news! */
+ }
skb->dev = dev; /* Mark as being used by this device. */
#if LINUX_VERSION_CODE > 0x10300
skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
- vp->rx_ring[entry].addr = cpu_to_le32(virt_to_bus(skb->tail));
+ vp->rx_ring[entry].addr = virt_to_bus(skb->tail);
#else
vp->rx_ring[entry].addr = virt_to_bus(skb->data);
#endif
@@ -1906,6+1859,12 @@ boomerang_rx(struct device *dev) vp->rx_ring[entry].status = 0; /* Clear complete bit. */
outw(UpUnstall, ioaddr + EL3_CMD);
}
+
+ if (vp->dirty_rx >= RX_RING_SIZE ) {
+ vp->cur_rx -= RX_RING_SIZE;
+ vp->dirty_rx -= RX_RING_SIZE;
+ }
+
return 0;
}
@@ -1913,7+1872,7 @@ static int vortex_close(struct device *dev)
{
struct vortex_private *vp = (struct vortex_private *)dev->priv;
- long ioaddr = dev->base_addr;
+ int ioaddr = dev->base_addr;
int i;
dev->start = 0;
@@ -1975,7+1934,8 @@ vortex_close(struct device *dev) return 0;
}
-static struct net_device_stats *vortex_get_stats(struct device *dev)
+static struct enet_statistics *
+vortex_get_stats(struct device *dev)
{
struct vortex_private *vp = (struct vortex_private *)dev->priv;
unsigned long flags;
@@ -1996,7+1956,7 @@ static struct net_device_stats *vortex_get_stats(struct device *dev) table. This is done by checking that the ASM (!) code generated uses
atomic updates with '+='.
*/
-static void update_stats(long ioaddr, struct device *dev)
+static void update_stats(int ioaddr, struct device *dev)
{
struct vortex_private *vp = (struct vortex_private *)dev->priv;
@@ -2031,7+1991,7 @@ static void update_stats(long ioaddr, struct device *dev) static int vortex_ioctl(struct device *dev, struct ifreq *rq, int cmd)
{
struct vortex_private *vp = (struct vortex_private *)dev->priv;
- long ioaddr = dev->base_addr;
+ int ioaddr = dev->base_addr;
u16 *data = (u16 *)&rq->ifr_data;
int phy = vp->phys[0] & 0x1f;
@@ -2040,7+2000,7 @@ static int vortex_ioctl(struct device *dev, struct ifreq *rq, int cmd) dev->name, rq->ifr_ifrn.ifrn_name, cmd,
data[0], data[1], data[2], data[3]);
- switch(cmd) {
+ switch(cmd) {
case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */
data[0] = phy;
case SIOCDEVPRIVATE+1: /* Read the specified MII register. */
@@ -2065,7+2025,7 @@ static int vortex_ioctl(struct device *dev, struct ifreq *rq, int cmd) static void
set_rx_mode(struct device *dev)
{
- long ioaddr = dev->base_addr;
+ int ioaddr = dev->base_addr;
int new_mode;
if (dev->flags & IFF_PROMISC) {
@@ -2108,11+2068,11 @@ set_multicast_list(struct device *dev, int num_addrs, void *addrs)
/* Generate the preamble required for initial synchronization and
a few older transceivers. */
-static void mdio_sync(long ioaddr, int bits)
+static void mdio_sync(int ioaddr, int bits)
{
- long mdio_addr = ioaddr + Wn4_PhysicalMgmt;
+ int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
- /* Establish sync by sending at least 32 logic ones. */
+ /* Establish sync by sending at least 32 logic ones. */
while (-- bits >= 0) {
outw(MDIO_DATA_WRITE1, mdio_addr);
mdio_delay();
@@ -2121,12+2081,12 @@ static void mdio_sync(long ioaddr, int bits) }
}
-static int mdio_read(long ioaddr, int phy_id, int location)
+static int mdio_read(int ioaddr, int phy_id, int location)
{
int i;
int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
unsigned int retval = 0;
- long mdio_addr = ioaddr + Wn4_PhysicalMgmt;
+ int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
if (mii_preamble_required)
mdio_sync(ioaddr, 32);
@@ -2150,10+2110,10 @@ static int mdio_read(long ioaddr, int phy_id, int location) return retval>>1 & 0xffff;
}
-static void mdio_write(long ioaddr, int phy_id, int location, int value)
+static void mdio_write(int ioaddr, int phy_id, int location, int value)
{
int write_cmd = 0x50020000 | (phy_id << 23) | (location << 18) | value;
- long mdio_addr = ioaddr + Wn4_PhysicalMgmt;
+ int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
int i;
if (mii_preamble_required)
@@ -2206,7+2166,7 @@ cleanup_module(void) * Local variables:
* compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
* SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c"
- * cardbus-compile-command: "gcc -DCARDBUS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c -o 3c575_cb.o -I/usr/src/pcmcia-cs-3.0.5/include/"
+ * compile-command-alt1: "gcc -DCARDBUS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c -o 3c59x_cb.o"
* c-indent-level: 4
* c-basic-offset: 4
* tab-width: 4
*/
#include <linux/module.h>
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/net.h>
*/
#include <linux/module.h>
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/net.h>
#include <linux/string.h>
#include <linux/sched.h>
#include <linux/kernel.h>
-#include <linux/head.h>
#include <linux/types.h>
#include <linux/ioport.h>
#include <linux/delay.h>
@@ -555,6+555,7 @@ static struct signature { } signatures[] =
{
{ "Adaptec AHA-1520 BIOS", 0x102e, 21 }, /* Adaptec 152x */
+ { "Adaptec AHA-1520B", 0x0b, 19 }, /* Adaptec 152x rev B */
{ "Adaptec ASW-B626 BIOS", 0x1029, 21 }, /* on-board controller */
{ "Adaptec BIOS: ASW-B626", 0x0f, 22 }, /* on-board controller */
{ "Adaptec ASW-B626 S2", 0x2e6c, 19 }, /* on-board controller */
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/head.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/ioport.h>
#endif
#include <linux/kernel.h>
-#include <linux/head.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/version.h>
#include <linux/kernel.h>
-#include <linux/head.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/string.h>
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/head.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/head.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/pci.h>
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/head.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/pci.h>
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/head.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/timer.h>
-#include <linux/head.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/genhd.h>
#include <stdarg.h>
#include <linux/kernel.h>
-#include <linux/head.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/sched.h>
@@ -399,7+399,7 @@ static void fbcon_setup(int con, int init, int logo) int old_rows, old_cols;
unsigned short *save = NULL, *r, *q;
/* Only if not module */
- extern int initmem_freed;
+ int initmem_freed = 1;
struct fbcon_font_desc *font;
if (con != fg_console || initmem_freed || p->type == FB_TYPE_TEXT)
logo = 0;
O_TARGET := umsdos.o
O_OBJS := dir.o file.o inode.o ioctl.o mangle.o namei.o \
- rdir.o symlink.o emd.o
-
-#check.o
+ rdir.o symlink.o emd.o check.o
M_OBJS := $(O_TARGET)
/*
* linux/fs/umsdos/check.c
+ *
+ * Sanity-checking code
*/
#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 <asm/system.h>
+#ifdef CHECK_PAGE_TABLES
static int check_one_table (struct pde *page_dir)
{
if (pgd_none (*page_dir))
@@ -52,3+54,181 @@ void check_page_tables (void) printk ("\nError MM %d\n", err);
}
}
+#endif
+
+
+#if UMS_DEBUG
+/*
+ * check a superblock
+ */
+
+void check_sb (struct super_block *sb, const char c)
+{
+ if (sb) {
+ Printk ((" (has %c_sb=%d, %d)",
+ c, MAJOR (sb->s_dev), MINOR (sb->s_dev)));
+ } else {
+ Printk ((" (%c_sb is NULL)", c));
+ }
+}
+
+/*
+ * check an inode
+ */
+
+void check_inode (struct inode *inode)
+{
+ if (inode) {
+ Printk ((KERN_DEBUG "* inode is %lu (i_count=%d)",
+ inode->i_ino, inode->i_count));
+ check_sb (inode->i_sb, 'i');
+
+ if (inode->i_dentry.next) { /* FIXME: does this work ? */
+ Printk ((" (has i_dentry)"));
+ } else {
+ Printk ((" (NO i_dentry)"));
+ }
+
+ if (inode->i_op == NULL) {
+ Printk ((" (i_op is NULL)\n"));
+ } else if (inode->i_op == &umsdos_dir_inode_operations) {
+ Printk ((" (i_op is umsdos_dir_inode_operations)\n"));
+ } else if (inode->i_op == &umsdos_file_inode_operations) {
+ Printk ((" (i_op is umsdos_file_inode_operations)\n"));
+ } else if (inode->i_op == &umsdos_file_inode_operations_no_bmap) {
+ Printk ((" (i_op is umsdos_file_inode_operations_no_bmap)\n"));
+ } else if (inode->i_op == &umsdos_file_inode_operations_readpage) {
+ Printk ((" (i_op is umsdos_file_inode_operations_readpage)\n"));
+ } else if (inode->i_op == &umsdos_rdir_inode_operations) {
+ Printk ((" (i_op is umsdos_rdir_inode_operations)\n"));
+ } else if (inode->i_op == &umsdos_symlink_inode_operations) {
+ Printk ((" (i_op is umsdos_symlink_inode_operations)\n"));
+ } else {
+ Printk ((" (i_op is UNKNOWN: %p)\n", inode->i_op));
+ }
+ } else {
+ Printk ((KERN_DEBUG "* inode is NULL\n"));
+ }
+}
+
+/*
+ * checks all inode->i_dentry
+ *
+ */
+void checkd_inode (struct inode *inode)
+{
+ struct dentry *ret;
+ struct list_head *cur;
+ int count = 0;
+ if (!inode) {
+ printk (KERN_ERR "checkd_inode: inode is NULL!\n");
+ return;
+ }
+
+ Printk ((KERN_DEBUG "checkd_inode: inode %lu\n", inode->i_ino));
+ cur = inode->i_dentry.next;
+ while (count++ < 10) {
+ PRINTK (("1..."));
+ if (!cur) {
+ Printk ((KERN_ERR "checkd_inode: *** NULL reached. exit.\n"));
+ return;
+ }
+ PRINTK (("2..."));
+ ret = list_entry (cur, struct dentry, d_alias);
+ PRINTK (("3..."));
+ if (cur == cur->next) {
+ Printk ((KERN_DEBUG "checkd_inode: *** cur=cur->next: normal exit.\n"));
+ return;
+ }
+ PRINTK (("4..."));
+ if (!ret) {
+ Printk ((KERN_ERR "checkd_inode: *** ret dentry is NULL. exit.\n"));
+ return;
+ }
+ PRINTK (("5... (ret=%p)...", ret));
+ PRINTK (("5.1.. (ret->d_dname=%p)...", &(ret->d_name)));
+ PRINTK (("5.1.1. (ret->d_dname.len=%d)...", (int) ret->d_name.len));
+ PRINTK (("5.1.2. (ret->d_dname.name=%c)...", ret->d_name.name));
+ Printk ((KERN_DEBUG "checkd_inode: i_dentry is %.*s\n", (int) ret->d_name.len, ret->d_name.name));
+ PRINTK (("6..."));
+ cur = cur->next;
+ PRINTK (("7..."));
+#if 1
+ Printk ((KERN_DEBUG "checkd_inode: *** finished after count 1 (operator forced)\n"));
+ return;
+#endif
+ }
+ Printk ((KERN_ERR "checkd_inode: *** OVER LIMIT (loop?) !\n"));
+ return;
+}
+
+/*
+ * internal part of check_dentry. does the real job.
+ *
+ */
+
+void check_dent_int (struct dentry *dentry, int parent)
+{
+ if (parent) {
+ Printk ((KERN_DEBUG "* parent(%d) dentry: %.*s\n",
+ parent, (int) dentry->d_name.len, dentry->d_name.name));
+ } else {
+ Printk ((KERN_DEBUG "* checking dentry: %.*s\n",
+ (int) dentry->d_name.len, dentry->d_name.name));
+ }
+ check_inode (dentry->d_inode);
+ Printk ((KERN_DEBUG "* d_count=%d", dentry->d_count));
+ check_sb (dentry->d_sb, 'd');
+ if (dentry->d_op == NULL) {
+ Printk ((" (d_op is NULL)\n"));
+ } else {
+ Printk ((" (d_op is UNKNOWN: %p)\n", dentry->d_op));
+ }
+}
+
+/*
+ * checks dentry with full traceback to root and prints info. Limited to 10 recursive depths to avoid infinite loops.
+ *
+ */
+
+void check_dentry_path (struct dentry *dentry, const char *desc)
+{
+ int count=0;
+ Printk ((KERN_DEBUG "*** check_dentry_path: %.60s\n", desc));
+
+ if (!dentry) {
+ Printk ((KERN_DEBUG "*** checking dentry... it is NULL !\n"));
+ return;
+ }
+ if (IS_ERR(dentry)) {
+ Printk ((KERN_DEBUG "*** checking dentry... it is ERR(%ld) !\n",
+ PTR_ERR(dentry)));
+ return;
+ }
+
+ while (dentry && count < 10) {
+ check_dent_int (dentry, count++);
+ if (dentry == dentry->d_parent) {
+ Printk ((KERN_DEBUG "*** end checking dentry (root reached ok)\n"));
+ break;
+ }
+ dentry = dentry->d_parent;
+ }
+
+ if (count >= 10) { /* if infinite loop detected */
+ Printk ((KERN_ERR
+ "*** WARNING ! INFINITE LOOP ! check_dentry_path aborted !\n"));
+ }
+
+ if (!dentry) {
+ Printk ((KERN_ERR
+ "*** WARNING ! NULL dentry ! check_dentry_path aborted !\n"));
+ }
+}
+#else
+void check_sb (struct super_block *sb, const char c) {};
+void check_inode (struct inode *inode) {};
+void checkd_inode (struct inode *inode) {};
+void check_dentry_path (struct dentry *dentry, const char *desc) {};
+#endif /* UMS_DEBUG */
+
-/*
- * linux/fs/umsdos/dir.c
- *
- * Written 1993 by Jacques Gelinas
- * Inspired from linux/fs/msdos/... : Werner Almesberger
- *
- * Extended MS-DOS directory handling functions
- */
-
-#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>
-
-#include <asm/uaccess.h>
-
-#define UMSDOS_SPECIAL_DIRFPOS 3
-extern struct inode *pseudo_root;
-
-
-/* P.T.Waltenberg
- * I've retained this to facilitate the lookup of some of the hard-wired files/directories UMSDOS
- * uses. It's easier to do once than hack all the other instances. Probably safer as well
- */
-
-/*
- * d_dir is directory to search for file, name&len define the file.
- * compat_umsdos_real_lookup returns dentry pointing to wanted file,
- * or NULL if not found. Calling code is respondible to call fin_dentry (result)
- */
-
-struct dentry *compat_umsdos_real_lookup (struct dentry *d_dir, const char *name, int len)
-{
- int rv;
- struct dentry *dentry;
-
- PRINTK ((KERN_DEBUG "compat_umsdos_real_lookup !!CNTx!!: start\n"));
-
- check_dentry_path (d_dir, "compat_umsdos_real_lookup B4 dir");
- dentry = creat_dentry (name, len, NULL, d_dir);
-
-/* check_dentry_path (dentry, "compat_umsdos_real_lookup B4");*/
-
- rv = umsdos_real_lookup (d_dir->d_inode, dentry);
- check_dentry_path (dentry, "compat_umsdos_real_lookup END");
-
- if (rv) {
- printk (KERN_WARNING "compat_umsdos_real_lookup failed with %d\n", rv);
- return NULL;
- }
-
-
- PRINTK ((KERN_DEBUG "compat_umsdos_real_lookup !!CNTx!!: end\n"));
-
- if (dentry->d_inode) return dentry; /* all OK */
-
- /* otherwise, we have a negative dentry. return NULL */
- Printk ((KERN_DEBUG "compat_umsdos_real_lookup: negative dentry - file not found\n"));
- fin_dentry (dentry);
- return NULL;
-}
-
-
-int compat_msdos_create (struct inode *dir, const char *name, int len, int mode, struct inode **inode)
-{
- int rv;
- struct dentry *dentry, *d_dir;
-
- check_inode (dir);
- d_dir = geti_dentry (dir);
- dentry = creat_dentry (name, len, NULL, d_dir);
- check_dentry_path (dentry, "compat_msdos_create START");
- rv = msdos_create (dir, dentry, mode);
- check_dentry_path (dentry, "compat_msdos_create END");
- if (inode != NULL)
- *inode = dentry->d_inode;
-
- check_inode (dir);
- return rv;
-}
-
-
-/*
- * So grep * doesn't complain in the presence of directories.
- */
-
-int dummy_dir_read (struct file *filp, char *buff, size_t size, loff_t *count)
-{
- return -EISDIR;
-}
-
-
-struct UMSDOS_DIR_ONCE {
- void *dirbuf;
- filldir_t filldir;
- int count;
- int stop;
-};
-
-/*
- * Record a single entry the first call.
- * Return -EINVAL the next one.
- * NOTE: filldir DOES NOT use a dentry
- */
-
-static int umsdos_dir_once ( void *buf,
- const char *name,
- int len,
- off_t offset,
- ino_t ino)
-{
- int ret = -EINVAL;
- struct UMSDOS_DIR_ONCE *d = (struct UMSDOS_DIR_ONCE *) buf;
-
- if (d->count == 0) {
- PRINTK ((KERN_DEBUG "dir_once :%.*s: offset %Ld\n", len, name, offset));
- ret = d->filldir (d->dirbuf, name, len, offset, ino);
- d->stop = ret < 0;
- d->count = 1;
- }
- return ret;
-}
-
-
-/*
- * Read count directory entries from directory filp
- * Return a negative value from linux/errno.h.
- * Return > 0 if success (The amount of byte written by filldir).
- *
- * 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 */
- void *dirbuf, /* Will hold count directory entry */
- /* but filled by the filldir function */
- int internal_read, /* Called for internal purpose */
- struct umsdos_dirent *u_entry, /* Optional umsdos entry */
- int follow_hlink,
- filldir_t filldir)
-{
- int ret = 0;
- struct dentry *old_dent;
-
- umsdos_startlookup (dir);
- if (filp->f_pos == UMSDOS_SPECIAL_DIRFPOS
- && pseudo_root
- && dir == pseudo_root
- && !internal_read) {
-
- Printk (("umsdos_readdir_x: what UMSDOS_SPECIAL_DIRFPOS /mn/?\n"));
- /*
- * 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.
- */
- if (filldir (dirbuf, "DOS", 3, UMSDOS_SPECIAL_DIRFPOS, UMSDOS_ROOT_INO) == 0) {
- filp->f_pos++;
- }
- } else if (filp->f_pos < 2 || (dir->i_ino != UMSDOS_ROOT_INO && filp->f_pos == 32)) {
-
- /* FIXME: that was in 2.0.x: else if (filp->f_pos < 2 || (dir != dir->i_sb->s_mounted && filp->f_pos == 32))
- * I'm probably screwing up pseudo-root and stuff with this. It needs proper fix.
- */
-
-
- /* #Specification: readdir / . and ..
- * The msdos filesystem manages 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.
- *
- * Now (linux 1.3), umsdos_readdir can read more than one
- * entry even if we limit (umsdos_dir_once) to only one:
- * It skips over hidden file. So we switch to
- * UMSDOS_SPECIAL_DIRFPOS as soon as we have read successfully
- * the .. entry.
- */
- int last_f_pos = filp->f_pos;
- struct UMSDOS_DIR_ONCE bufk;
-
- Printk (("umsdos_readdir_x: . or .. /mn/?\n"));
-
- bufk.dirbuf = dirbuf;
- bufk.filldir = filldir;
- bufk.count = 0;
-
- ret = fat_readdir (filp, &bufk, umsdos_dir_once);
- if (last_f_pos > 0 && filp->f_pos > last_f_pos)
- filp->f_pos = UMSDOS_SPECIAL_DIRFPOS;
- if (u_entry != NULL)
- u_entry->flags = 0;
- } else {
- Printk (("umsdos_readdir_x: normal file /mn/?\n"));
- old_dent = filp->f_dentry; /* save dentry of directory */
-
- if (fix_emd_filp (filp) == 0) {
- off_t start_fpos = filp->f_pos;
-
- Printk (("umsdos_readdir_x: emd_dir->i_ino=%ld\n", filp->f_dentry->d_inode->i_ino));
- if (filp->f_pos <= UMSDOS_SPECIAL_DIRFPOS + 1)
- filp->f_pos = 0;
- Printk (("f_pos %Ld i_size %ld\n", filp->f_pos, filp->f_dentry->d_inode->i_size));
- ret = 0;
- while (filp->f_pos < filp->f_dentry->d_inode->i_size) {
- struct umsdos_dirent entry;
- off_t cur_f_pos = filp->f_pos;
-
- if (umsdos_emd_dir_readentry (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 dentry *d_dir, *dret;
-
- umsdos_parse (entry.name, entry.name_len, &info);
- info.f_pos = cur_f_pos;
- umsdos_manglename (&info);
- d_dir = geti_dentry (dir);
- dret = compat_umsdos_real_lookup (d_dir, info.fake.fname, info.fake.len);
-
- Printk (("Looking for inode of %s dret %p flags %d\n", info.fake.fname, dret, entry.flags));
- if (dret && !IS_ERR(dret)
- && (entry.flags & UMSDOS_HLINK)
- && follow_hlink) {
- dret = umsdos_solve_hlink (dret);
- }
-
- if (dret && !IS_ERR(dret)) {
- /* #Specification: pseudo root / reading real root
- * The pseudo root (/linux) is logically
- * erased from the real root. This means that
- * ls /DOS, won't show "linux". This avoids
- * infinite recursion (/DOS/linux/DOS/linux/...) while
- * walking the file system.
- */
- if (dret->d_inode != pseudo_root
- && (internal_read || !(entry.flags & UMSDOS_HIDDEN))) {
- Printk ((KERN_DEBUG "filldir now\n"));
- if (filldir (dirbuf, entry.name, entry.name_len, cur_f_pos, dret->d_inode->i_ino) < 0) {
- filp->f_pos = cur_f_pos;
- }
- Printk (("Found ino %ld ", dret->d_inode->i_ino));
- if (u_entry != NULL)
- *u_entry = entry;
- fin_dentry (dret);
- break;
- }
- fin_dentry (dret);
- } else {
- /* #Specification: umsdos / readdir / not in MSDOS
- * During a readdir operation, if the file is not
- * in the MS-DOS directory any more, the entry is
- * removed from the EMD file silently.
- */
- Printk (("'Silently' removing EMD for file\n"));
- ret = umsdos_writeentry (dir, filp->f_dentry->d_inode, &info, 1);
- if (ret != 0) {
- break;
- }
- }
- }
- }
- /*
- * If the fillbuf has failed, f_pos is back to 0.
- * To avoid getting back into the . and .. state
- * (see comments at the beginning), we put back
- * the special offset.
- */
- if (filp->f_pos == 0)
- filp->f_pos = start_fpos;
- Printk ((KERN_DEBUG "dir.c: putting emd_dir %lu with i_count=%d\n", filp->f_dentry->d_inode->i_ino, filp->f_dentry->d_inode->i_count));
-
- fin_dentry (filp->f_dentry);
- filp->f_dentry = old_dent; /* restore dentry of directory */
- }
- }
- umsdos_endlookup (dir);
-
- Printk ((KERN_DEBUG "read dir %p pos %Ld 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 or positive if successful.
- */
-
-static int UMSDOS_readdir ( struct file *filp, /* Point to a directory which is read. */
- void *dirbuf, /* Will hold directory entries */
- filldir_t filldir)
-{
- struct inode *dir = filp->f_dentry->d_inode;
- int ret = 0;
- int count = 0;
- struct UMSDOS_DIR_ONCE bufk;
-
- bufk.dirbuf = dirbuf;
- bufk.filldir = filldir;
- bufk.stop = 0;
-
- Printk (("UMSDOS_readdir in\n"));
- while (ret == 0 && bufk.stop == 0) {
- struct umsdos_dirent entry;
-
- bufk.count = 0;
- PRINTK (("UMSDOS_readdir: calling _x (%p,%p,%p,%d,%p,%d,%p)\n", dir, filp, &bufk, 0, &entry, 1, umsdos_dir_once));
- ret = umsdos_readdir_x (dir, filp, &bufk, 0, &entry, 1, umsdos_dir_once);
- if (bufk.count == 0)
- break;
- count += bufk.count;
- }
- /* FIXME: do we first need to deallocate old dentry ? look/check. see also all other instances of fix_emd_filp */
- Printk (("UMSDOS_readdir out %d count %d pos %Ld\n", ret, count, filp->f_pos));
- return count ? : 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 modifies the state of a dir inode. It decides
- * whether the dir is a UMSDOS or DOS directory. This is done
- * deeper in umsdos_patch_inode() called at the end of this function.
- *
- * Because it is does disk access, umsdos_patch_inode() may block.
- * At the same time, another process may get here to initialise
- * the same directory inode. There are three 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 locked the inode, so we try
- * to lock it and check right afterward check whether
- * 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 memory to 4 MB 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 automatically.
- */
-
- 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 MS-DOS for mtime for directories
- * because the MS-DOS date on a directory is its
- * creation time (strange MSDOS behavior) which
- * corresponds to none of the three Unix time stamps.
- */
- if (S_ISREG (entry->mode))
- entry->mtime = inode->i_mtime;
- inode->i_mode = entry->mode;
- inode->i_rdev = to_kdev_t (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 / conversion mode
- * The msdos filesystem can do some inline conversion
- * of the data of a file. It can translate silently
- * from the MS-DOS text file format to the Unix one
- * (CRLF -> LF) while reading, and the reverse
- * while writing. This is activated using the mount
- * option conv=....
- *
- * This is not useful for Linux files in a promoted
- * directory. It can even be harmful. For this
- * reason, the binary (no conversion) mode is
- * always activated.
- */
- /* #Specification: umsdos / conversion mode / todo
- * A flag could be added to file and directories
- * forcing an automatic conversion mode (as
- * done with the msdos filesystem).
- *
- * This flag could be setup on a directory basis
- * (instead of file) and all files in it would
- * logically inherit it. If the conversion mode
- * is active (conv=) then the i_binary flag would
- * be left untouched in those directories.
- *
- * It was proposed that the sticky bit be used to set
- * this. A problem with that is that new files would
- * be written incorrectly. The other problem is that
- * the sticky bit has a meaning for directories. So
- * another bit should be used (there is some space
- * in the EMD file for it) and a special utility
- * would be used to assign the flag to a directory).
- * I don't think it is useful to assign this flag
- * on a single file.
- */
-
- MSDOS_I (inode)->i_binary = 1;
- /* #Specification: umsdos / i_nlink
- * The nlink field of an inode is maintained by the MSDOS file system
- * for directory and by UMSDOS for other files. 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 links
- * 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 (KERN_ERR "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 (KERN_WARNING "UMSDOS: emd_owner still 0?\n");
- }
-}
-
-
-
-struct UMSDOS_DIRENT_K {
- off_t f_pos; /* will hold the offset of the entry in EMD */
- ino_t ino;
-};
-
-
-/*
- * Just to record the offset of one entry.
- */
-
-static int umsdos_filldir_k ( void *buf,
- const char *name,
- int len,
- off_t offset,
- ino_t ino)
-{
- struct UMSDOS_DIRENT_K *d = (struct UMSDOS_DIRENT_K *) buf;
-
- d->f_pos = offset;
- d->ino = ino;
- return 0;
-}
-
-struct UMSDOS_DIR_SEARCH {
- struct umsdos_dirent *entry;
- int found;
- ino_t search_ino;
-};
-
-static int umsdos_dir_search ( void *buf,
- const char *name,
- int len,
- off_t offset,
- ino_t ino)
-{
- int ret = 0;
- struct UMSDOS_DIR_SEARCH *d = (struct UMSDOS_DIR_SEARCH *) buf;
-
- if (d->search_ino == ino) {
- d->found = 1;
- memcpy (d->entry->name, name, len);
- d->entry->name[len] = '\0';
- d->entry->name_len = len;
- ret = 1; /* So fat_readdir will terminate */
- }
- return ret;
-}
-
-
-
-/*
- * 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 (pseudo_root && 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); / * FIXME? */
- if (emddir == NULL) {
- /* This is a DOS directory. */
- struct UMSDOS_DIR_SEARCH bufk;
- struct file filp;
- struct dentry *i2e;
-
- i2e = creat_dentry ("@i2e.nul@", 9, dir, NULL);
-
- fill_new_filp (&filp, i2e);
-
- Printk ((KERN_ERR "umsdos_inode2entry emddir==NULL: WARNING: Known filp problem. segfaulting :) fixed ?/mn/\n"));
- filp.f_reada = 1;
- filp.f_pos = 0;
- bufk.entry = entry;
- bufk.search_ino = inode->i_ino;
- fat_readdir (&filp, &bufk, umsdos_dir_search);
- if (bufk.found) {
- ret = 0;
- inode->u.umsdos_i.i_dir_owner = dir->i_ino;
- inode->u.umsdos_i.i_emd_owner = 0;
- umsdos_setup_dir_inode (inode);
- }
- } else {
- /* skip . and .. see umsdos_readdir_x() */
- struct file filp;
- struct dentry *i2e;
-
- i2e = creat_dentry ("@i2e.nn@", 8, dir, NULL);
- fill_new_filp (&filp, i2e);
-
- filp.f_reada = 1;
- filp.f_pos = UMSDOS_SPECIAL_DIRFPOS;
- Printk ((KERN_ERR "umsdos_inode2entry skip...: WARNING: Known filp problem. segfaulting :) fixed ?/mn/\n"));
- while (1) {
- struct UMSDOS_DIRENT_K bufk;
- struct dentry *old_dent;
-
- old_dent = filp.f_dentry;
- if (umsdos_readdir_x (dir, &filp, &bufk, 1, entry, 0, umsdos_filldir_k) < 0) {
- printk ("UMSDOS: can't locate inode %ld in EMD file???\n", inode->i_ino);
- break;
- } else if (bufk.ino == inode->i_ino) {
- ret = 0;
- filp.f_dentry = old_dent;
- umsdos_lookup_patch (dir, inode, entry, bufk.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=-99;
- struct dentry *dret, *d_dir = creat_dentry ("@d_dir2@", 7, dir, NULL);
-
- umsdos_patch_inode (dir, NULL, 0);
- /* FIXME */
- dret = compat_umsdos_real_lookup (d_dir, "..", 2);
- Printk (("result %p %p ", dret, *result));
- if (dret) {
- struct inode *adir;
- *result = dret->d_inode;
- adir = *result;
-
- ret = umsdos_inode2entry (adir, dir, entry);
- fin_dentry (dret);
- }
- 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;
- struct inode *root_inode;
- char *bpath = (char *) kmalloc (PATH_MAX, GFP_KERNEL);
-
- root_inode = iget (inode->i_sb, UMSDOS_ROOT_INO);
- 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: ", entry.name_len, ptbpath));
- }
- }
- } else {
- inc_count (dir);
- }
- if (ret == 0) {
- while (dir != root_inode) {
- struct inode *adir;
-
- ret = umsdos_locate_ancestor (dir, &adir, &entry);
- /* iput (dir); FIXME */
- 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: ", entry.name_len, ptbpath));
- } else {
- break;
- }
- }
- }
- strcpy (path, ptbpath);
- kfree (bpath);
- }
- Printk (("\n"));
- /* iput (dir); / * FIXME?? */
- return ret;
-}
-
-
-/*
- * Return != 0 if an entry is the pseudo DOS entry in the pseudo root.
- */
-
-int umsdos_is_pseudodos ( struct inode *dir,
- struct dentry *dentry)
-{
- /* #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 pseudo_root
- && dir == pseudo_root
- && dentry->d_name.len == 3
- && dentry->d_name.name[0] == 'D'
- && dentry->d_name.name[1] == 'O'
- && dentry->d_name.name[2] == 'S';
-}
-
-
-/*
- * Check if a file exists in the current directory.
- * Return 0 if OK, negative error code if not (ex: -ENOENT).
- *
- * fills dentry->d_inode with found inode, and increments its count.
- * if not found, return -ENOENT.
- */
-
-int umsdos_lookup_x ( struct inode *dir,
- struct dentry *dentry,
- int nopseudo) /* Don't care about pseudo root mode */
-{
- int ret = -ENOENT;
- struct inode *root_inode;
- int len = dentry->d_name.len;
- const char *name = dentry->d_name.name;
-
-
-#if UMS_DEBUG
- Printk ((KERN_DEBUG "umsdos_lookup_x: /mn/ name=%.*s, dir=%lu (i_count=%d), d_parent=%p\n", (int) dentry->d_name.len, dentry->d_name.name, dir->i_ino, dir->i_count, dentry->d_parent)); /* FIXME /mn/ debug only */
- if (dentry->d_parent)
- Printk ((KERN_DEBUG " d_parent is %.*s\n", (int) dentry->d_parent->d_name.len, dentry->d_parent->d_name.name)); /* FIXME : delme /mn/ */
-#endif
-
- root_inode = iget (dir->i_sb, UMSDOS_ROOT_INO);
- Printk ((KERN_ERR "umsdos_lookup_x (CNT!): entering root_count+1=%d, dir %lu _count=%d\n", root_inode->i_count, dir->i_ino, dir->i_count)); /* FIXME: DEBUG, DELME */
-
- dentry->d_inode = NULL;
- umsdos_startlookup (dir);
- if (len == 1 && name[0] == '.') {
- d_add (dentry, dir);
- inc_count (dir);
- ret = 0;
- } else if (len == 2 && name[0] == '.' && name[1] == '.') {
- if (pseudo_root && dir == pseudo_root) {
- /* #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;
- d_add (dentry, pseudo_root);
- inc_count (pseudo_root);
- } 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 as
- * permissions and ownership.
- */
- struct dentry *dret, *d_dir = creat_dentry ("@d_dir3@", 5, dir, NULL);
-
-
- dret = compat_umsdos_real_lookup (d_dir, "..", 2);
- Printk (("ancestor ret %p dir %p *result %p ", dret, dir, dentry->d_inode));
- if (dret
- && dentry->d_inode != root_inode
- && dentry->d_inode != pseudo_root) {
- struct inode *aadir;
- struct umsdos_dirent entry;
-
- Printk ((KERN_ERR "WARNING: umsdos_lookup_x: this won't work!\n"));
-
- dentry->d_inode = dret->d_inode; /* FIXME! this should be rewritten ! it won't work this way! */
-
- ret = umsdos_locate_ancestor (dentry->d_inode, &aadir, &entry);
- fin_dentry (dret);
- }
- }
- } else if (umsdos_is_pseudodos (dir, dentry)) {
- /* #Specification: pseudo root / lookup(DOS)
- * A lookup of DOS in the pseudo root will always succeed
- * and return the inode of the real root.
- */
- d_add (dentry, root_inode);
- inc_count (dentry->d_inode); /* FIXME?! */
- ret = 0;
- } else {
- struct umsdos_info info;
-
- ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
- if (ret == 0)
- ret = umsdos_findentry (dir, &info, 0);
- Printk (("lookup %.*s pos %lu ret %d len %d ", info.fake.len, 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 steps. 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 dentry *dret, *dir_dentry;
-
- dir_dentry = geti_dentry (dir);
- dret = compat_umsdos_real_lookup (dir_dentry, info.fake.fname, info.fake.len);
-
-
- PRINTK ((KERN_DEBUG "umsdos_lookup_x: compat_umsdos_real_lookup for %.*s returned %p\n", info.fake.len, info.fake.fname, dret));
-
- if (dret == NULL) {
- printk (KERN_WARNING "UMSDOS: Erase entry %.*s, out of sync with MS-DOS\n", info.fake.len, info.fake.fname);
- umsdos_delentry (dir, &info, S_ISDIR (info.entry.mode));
- } else {
- Printk ((KERN_DEBUG "umsdos_lookup_x /mn/ debug: ino=%li\n", dret->d_inode->i_ino));
-
- umsdos_lookup_patch (dir, dret->d_inode, &info.entry, info.f_pos);
- Printk (("lookup ino %ld flags %d\n", dret->d_inode->i_ino, info.entry.flags));
- if (info.entry.flags & UMSDOS_HLINK) {
- dret = umsdos_solve_hlink (dret);
- }
- if (pseudo_root && dret->d_inode == 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 links
- * which are recorded independently of the pseudo-root
- * mode.
- */
- Printk ((KERN_ERR "umsdos_lookup_x: warning: untested /mn/ Pseudo_root thingy\n"));
- /* iput (pseudo_root); / * FIXME?? */
- d_instantiate (dentry, NULL); /* negative lookup */
- ret = -ENOENT;
- } else {
- /* We've found it OK. Now put inode in dentry. */
- inc_count (dret->d_inode); /* lookup should return incremented i_count */
- d_add (dentry, dret->d_inode);
- }
- fin_dentry (dret);
- }
- }
- }
- umsdos_endlookup (dir);
- iput (root_inode); /* pair to iget() above.WHY is it not needed ?! */
- PRINTK ((KERN_DEBUG "umsdos_lookup_x: returning %d : name=%.*s (i_count=%d), dir=%lu (i_count=%d)\n", ret, (int) dentry->d_name.len, dentry->d_name.name, dentry->d_inode->i_count, dir->i_ino, dir->i_count));
- Printk ((KERN_ERR "umsdos_lookup_x (CNT!): exiting root_count=%d, dir %lu _count=%d\n", root_inode->i_count, dir->i_ino, dir->i_count)); /* FIXME: DEBUG, DELME */
- return ret;
-}
-
-
-/*
- * Check whether a file exists in the current directory.
- * Return 0 if OK, negative error code if not (ex: -ENOENT).
- *
- * called by VFS. should fill dentry->d_inode (via d_add), and
- * set (increment) dentry->d_inode->i_count.
- *
- */
-
-int UMSDOS_lookup ( struct inode *dir,
- struct dentry *dentry)
-{
- int ret;
-
- ret = umsdos_lookup_x (dir, dentry, 0);
-
- if (ret == -ENOENT) {
- Printk ((KERN_DEBUG "UMSDOS_lookup: converting -ENOENT to negative dentry !\n"));
- d_add (dentry, NULL); /* Create negative dentry if not found. */
- ret = 0;
- }
-
- return ret;
-}
-
-
-/*
- * gets dentry which points to pseudo-hardlink
- *
- * it should try to find file it points to
- * if file is found, it should dput() original dentry and return new one (with d_count = i_count = 1)
- * Otherwise, it should return with error, with dput()ed original dentry.
- *
- */
-
-struct dentry *umsdos_solve_hlink (struct dentry *hlink)
-{
- struct dentry *base = hlink->d_sb->s_root; /* root is our root for resolving pseudo-hardlink */
- struct dentry *final = NULL;
- struct inode *result;
- int ret = -EIO;
- struct dentry *dentry_dst, *d_dir;
- char *path;
-
- check_dentry_path (hlink, "HLINK BEGIN hlink");
-
- path = (char *) kmalloc (PATH_MAX, GFP_KERNEL);
-
- result = NULL;
-
- if (path == NULL) {
- final = ERR_PTR (-ENOMEM);
- } else {
- struct file filp;
- fill_new_filp (&filp, hlink);
- filp.f_flags = O_RDONLY;
- filp.f_pos = 0;
-
- Printk (("hlink2inode "));
- if (umsdos_file_read_kmem (&filp, path, hlink->d_inode->i_size) == hlink->d_inode->i_size) {
- struct inode *dir;
- char *pt = path;
-
- dir = base->d_inode; /* start at root inode */
- path[hlink->d_inode->i_size] = '\0';
- inc_count (dir); /* since we're going to iput() it in the loop below... */
-
- while (1) {
- char *start = pt;
- int len;
-
- while (*pt != '\0' && *pt != '/') pt++;
- len = (int) (pt - start);
- if (*pt == '/') *pt++ = '\0';
-
- d_dir = geti_dentry (dir);
- dentry_dst = creat_dentry (start, len, NULL, d_dir);
- if (dir->u.umsdos_i.i_emd_dir == 0) {
- /* This is a DOS directory */
-
- Printk (("hlink2inode /mn/: doing umsdos_rlookup_x on %.*s\n", (int) dentry_dst->d_name.len, dentry_dst->d_name.name));
- ret = umsdos_rlookup_x (dir, dentry_dst, 1);
- } else {
- Printk (("hlink2inode /mn/: doing umsdos_lookup_x on %.*s\n", (int) dentry_dst->d_name.len, dentry_dst->d_name.name));
- ret = umsdos_lookup_x (dir, dentry_dst, 1);
- }
-
- iput (dir); /* dir no longer needed */
- Printk ((" returned %d\n", ret));
- result = dentry_dst->d_inode;
- inc_count (result); /* we need inode to survive. We'll iput it in next loop */
-
- fin_dentry (dentry_dst); /* no longer needed - this is pair to creat_dentry */
-
- Printk (("h2n lookup :%s: -> %d ", start, ret));
-
- if (ret != 0) {
- iput (result); /* we have no longer any use for it... */
- final = ERR_PTR (ret); /* path componenet not found ! */
- break;
- } else {
- if (*pt != '\0') {
- dir = result;
- } else { /* we're finished! */
- final = creat_dentry (hlink->d_name.name, hlink->d_name.len, result, hlink->d_parent);
- break;
- }
- }
- } /* end while */
- } else {
- Printk (("umsdos_solve_hlink: failed reading pseudolink!\n"));
- }
-
- Printk (("hlink2inode ret = %d %p -> %p\n", ret, hlink, result));
- kfree (path);
- }
-
- fin_dentry (hlink); /* original hlink no longer needed */
- check_dentry_path (hlink, "HLINK FIN hlink");
- check_dentry_path (final, "HLINK RET final");
- return final;
-}
-
-
-static struct file_operations umsdos_dir_operations =
-{
- NULL, /* lseek - default */
- dummy_dir_read, /* read */
- NULL, /* write - bad */
- UMSDOS_readdir, /* readdir */
- NULL, /* poll - default */
- UMSDOS_ioctl_dir, /* ioctl - default */
- NULL, /* mmap */
- NULL, /* no special open code */
- NULL, /* flush */
- NULL, /* no special release code */
- NULL /* fsync *//* in original NULL. changed to file_fsync. FIXME? /mn/ */
-};
-
-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, /* followlink */
- generic_readpage, /* readpage *//* in original NULL. changed to generic_readpage. FIXME? /mn/ */
- NULL, /* writepage */
- fat_bmap, /* bmap *//* in original NULL. changed to fat_bmap. FIXME? /mn/ */
- NULL, /* truncate */
- NULL, /* permission */
- NULL, /* smap */
- NULL, /* updatepage */
- NULL, /* revalidate */
-
-};
+/*
+ * linux/fs/umsdos/dir.c
+ *
+ * Written 1993 by Jacques Gelinas
+ * Inspired from linux/fs/msdos/... : Werner Almesberger
+ *
+ * Extended MS-DOS directory handling functions
+ */
+
+#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>
+
+#include <asm/uaccess.h>
+
+#define UMSDOS_SPECIAL_DIRFPOS 3
+extern struct inode *pseudo_root;
+
+
+
+/*
+ * This needs to have the parent dentry passed to it.
+ * N.B. Try to get rid of this soon!
+ */
+int compat_msdos_create (struct inode *dir, const char *name, int len,
+ int mode, struct inode **inode)
+{
+ int ret;
+ struct dentry *dentry, *d_dir;
+
+ check_inode (dir);
+ ret = -ENOMEM;
+ d_dir = geti_dentry (dir);
+ if (!d_dir) {
+printk(KERN_ERR "compat_msdos_create: flaky i_dentry didn't work\n");
+ goto out;
+ }
+ dget(d_dir);
+ dentry = creat_dentry (name, len, NULL, d_dir);
+ dput(d_dir);
+ if (!dentry)
+ goto out;
+
+ check_dentry_path (dentry, "compat_msdos_create START");
+ ret = msdos_create (dir, dentry, mode);
+ check_dentry_path (dentry, "compat_msdos_create END");
+ if (ret)
+ goto out;
+ if (inode != NULL)
+ *inode = dentry->d_inode;
+
+ check_inode (dir);
+out:
+ return ret;
+}
+
+
+/*
+ * So grep * doesn't complain in the presence of directories.
+ */
+
+int dummy_dir_read (struct file *filp, char *buff, size_t size, loff_t *count)
+{
+ return -EISDIR;
+}
+
+
+struct UMSDOS_DIR_ONCE {
+ void *dirbuf;
+ filldir_t filldir;
+ int count;
+ int stop;
+};
+
+/*
+ * Record a single entry the first call.
+ * Return -EINVAL the next one.
+ * NOTE: filldir DOES NOT use a dentry
+ */
+
+static int umsdos_dir_once ( void *buf,
+ const char *name,
+ int len,
+ off_t offset,
+ ino_t ino)
+{
+ int ret = -EINVAL;
+ struct UMSDOS_DIR_ONCE *d = (struct UMSDOS_DIR_ONCE *) buf;
+
+ if (d->count == 0) {
+ PRINTK ((KERN_DEBUG "dir_once :%.*s: offset %Ld\n",
+ len, name, offset));
+ ret = d->filldir (d->dirbuf, name, len, offset, ino);
+ d->stop = ret < 0;
+ d->count = 1;
+ }
+ return ret;
+}
+
+
+/*
+ * Read count directory entries from directory filp
+ * Return a negative value from linux/errno.h.
+ * Return > 0 if success (the number of bytes written by filldir).
+ *
+ * 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, struct file *filp,
+ void *dirbuf, int internal_read,
+ struct umsdos_dirent *u_entry,
+ int follow_hlink, filldir_t filldir)
+{
+ struct dentry *demd;
+ off_t start_fpos;
+ int ret = 0;
+ struct file new_filp;
+
+ umsdos_startlookup (dir);
+
+ if (filp->f_pos == UMSDOS_SPECIAL_DIRFPOS &&
+ dir == pseudo_root && !internal_read) {
+
+Printk (("umsdos_readdir_x: what UMSDOS_SPECIAL_DIRFPOS /mn/?\n"));
+ /*
+ * 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.
+ */
+ if (filldir (dirbuf, "DOS", 3,
+ UMSDOS_SPECIAL_DIRFPOS, UMSDOS_ROOT_INO) == 0) {
+ filp->f_pos++;
+ }
+ goto out_end;
+ }
+
+ if (filp->f_pos < 2 ||
+ (dir->i_ino != UMSDOS_ROOT_INO && filp->f_pos == 32)) {
+
+ int last_f_pos = filp->f_pos;
+ struct UMSDOS_DIR_ONCE bufk;
+
+ Printk (("umsdos_readdir_x: . or .. /mn/?\n"));
+
+ bufk.dirbuf = dirbuf;
+ bufk.filldir = filldir;
+ bufk.count = 0;
+
+ ret = fat_readdir (filp, &bufk, umsdos_dir_once);
+ if (last_f_pos > 0 && filp->f_pos > last_f_pos)
+ filp->f_pos = UMSDOS_SPECIAL_DIRFPOS;
+ if (u_entry != NULL)
+ u_entry->flags = 0;
+ goto out_end;
+ }
+
+ Printk (("umsdos_readdir_x: normal file /mn/?\n"));
+
+ /* get the EMD dentry */
+ demd = umsdos_get_emd_dentry(filp->f_dentry);
+ ret = PTR_ERR(demd);
+ if (IS_ERR(demd))
+ goto out_end;
+ ret = 0;
+ if (!demd->d_inode)
+ goto out_dput;
+
+ /* set up our private filp ... */
+ fill_new_filp(&new_filp, demd);
+ new_filp.f_pos = filp->f_pos;
+ start_fpos = filp->f_pos;
+
+ if (new_filp.f_pos <= UMSDOS_SPECIAL_DIRFPOS + 1)
+ new_filp.f_pos = 0;
+Printk (("f_pos %Ld i_size %ld\n", new_filp.f_pos, demd->d_inode->i_size));
+ ret = 0;
+ while (new_filp.f_pos < demd->d_inode->i_size) {
+ off_t cur_f_pos = new_filp.f_pos;
+ struct umsdos_info info;
+ struct dentry *dret;
+ struct umsdos_dirent entry;
+
+ ret = -EIO;
+ if (umsdos_emd_dir_readentry (&new_filp, &entry) != 0)
+ break;
+
+ if (entry.name_len == 0)
+ goto remove_name;
+
+ umsdos_parse (entry.name, entry.name_len, &info);
+ info.f_pos = cur_f_pos;
+ umsdos_manglename (&info);
+ dret = umsdos_lookup_dentry(filp->f_dentry, info.fake.fname,
+ info.fake.len);
+ ret = PTR_ERR(dret);
+ if (IS_ERR(dret))
+ break;
+
+Printk (("Looking for inode of %s/%s, flags=%x\n",
+dret->d_parent->d_name.name, info.fake.fname, entry.flags));
+ if ((entry.flags & UMSDOS_HLINK) && follow_hlink) {
+ dret = umsdos_solve_hlink (dret);
+ ret = PTR_ERR(dret);
+ if (IS_ERR(dret))
+ break;
+ }
+
+ /* #Specification: pseudo root / reading real root
+ * The pseudo root (/linux) is logically
+ * erased from the real root. This means that
+ * ls /DOS, won't show "linux". This avoids
+ * infinite recursion (/DOS/linux/DOS/linux/...) while
+ * walking the file system.
+ */
+ if (dret->d_inode != pseudo_root &&
+ (internal_read || !(entry.flags & UMSDOS_HIDDEN))) {
+ Printk ((KERN_DEBUG "filldir now\n"));
+ if (filldir (dirbuf, entry.name, entry.name_len,
+ cur_f_pos, dret->d_inode->i_ino) < 0) {
+ new_filp.f_pos = cur_f_pos;
+ }
+Printk (("Found %s/%s(%ld)\n",
+dret->d_parent->d_name.name, dret->d_name.name, dret->d_inode->i_ino));
+ if (u_entry != NULL)
+ *u_entry = entry;
+ dput(dret);
+ break;
+ }
+ dput(dret);
+ continue;
+
+ remove_name:
+ /* #Specification: umsdos / readdir / not in MSDOS
+ * During a readdir operation, if the file is not
+ * in the MS-DOS directory any more, the entry is
+ * removed from the EMD file silently.
+ */
+ Printk (("'Silently' removing EMD for file\n"));
+ ret = umsdos_delentry(filp->f_dentry, &info, 1);
+ if (ret)
+ break;
+ continue;
+ }
+ /*
+ * If the fillbuf has failed, f_pos is back to 0.
+ * To avoid getting back into the . and .. state
+ * (see comments at the beginning), we put back
+ * the special offset.
+ */
+ filp->f_pos = new_filp.f_pos;
+ if (filp->f_pos == 0)
+ filp->f_pos = start_fpos;
+out_dput:
+ dput(demd);
+
+out_end:
+ umsdos_endlookup (dir);
+
+ Printk ((KERN_DEBUG "read dir %p pos %Ld 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 or positive if successful.
+ */
+
+static int UMSDOS_readdir (struct file *filp, void *dirbuf, filldir_t filldir)
+{
+ struct inode *dir = filp->f_dentry->d_inode;
+ int ret = 0, count = 0;
+ struct UMSDOS_DIR_ONCE bufk;
+
+ bufk.dirbuf = dirbuf;
+ bufk.filldir = filldir;
+ bufk.stop = 0;
+
+ Printk (("UMSDOS_readdir in\n"));
+ while (ret == 0 && bufk.stop == 0) {
+ struct umsdos_dirent entry;
+
+ bufk.count = 0;
+ PRINTK (("UMSDOS_readdir: calling _x (%p,%p,%p,%d,%p,%d,%p)\n",
+ dir, filp, &bufk, 0, &entry, 1, umsdos_dir_once));
+ ret = umsdos_readdir_x (dir, filp, &bufk, 0, &entry, 1,
+ umsdos_dir_once);
+ if (bufk.count == 0)
+ break;
+ count += bufk.count;
+ }
+ Printk (("UMSDOS_readdir out %d count %d pos %Ld\n",
+ ret, count, filp->f_pos));
+ return count ? : ret;
+}
+
+
+/*
+ * Complete the inode content with info from the EMD file.
+ *
+ * This function modifies the state of a dir inode. It decides
+ * whether the dir is a UMSDOS or DOS directory. This is done
+ * deeper in umsdos_patch_inode() called at the end of this function.
+ *
+ * Because it is does disk access, umsdos_patch_inode() may block.
+ * At the same time, another process may get here to initialise
+ * the same directory inode. There are three 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 locked the inode, so we try
+ * to lock it and check right afterward check whether
+ * 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 memory to 4 MB 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 automatically.
+ */
+
+void umsdos_lookup_patch (struct inode *dir, struct inode *inode,
+ struct umsdos_dirent *entry, off_t emd_pos)
+{
+ if (inode->i_sb != dir->i_sb)
+ goto out;
+ if (umsdos_isinit (inode))
+ goto out;
+
+ if (S_ISDIR (inode->i_mode))
+ umsdos_lockcreate (inode);
+ if (umsdos_isinit (inode))
+ goto out_unlock;
+
+ if (S_ISREG (entry->mode))
+ entry->mtime = inode->i_mtime;
+ inode->i_mode = entry->mode;
+ inode->i_rdev = to_kdev_t (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;
+
+ MSDOS_I (inode)->i_binary = 1;
+ /* #Specification: umsdos / i_nlink
+ * The nlink field of an inode is maintained by the MSDOS file system
+ * for directory and by UMSDOS for other files. 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 links
+ * 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 (KERN_ERR
+ "UMSDOS: lookup_patch entry->nlink < 1 ???\n");
+ }
+ }
+ umsdos_patch_inode (inode, dir, emd_pos);
+
+out_unlock:
+ if (S_ISDIR (inode->i_mode))
+ umsdos_unlockcreate (inode);
+ if (inode->u.umsdos_i.i_emd_owner == 0)
+ printk (KERN_WARNING "UMSDOS: emd_owner still 0?\n");
+out:
+ return;
+}
+
+
+/*
+ * The preferred interface to the above routine ...
+ */
+void umsdos_lookup_patch_new(struct dentry *dentry, struct umsdos_dirent *entry,
+ off_t emd_pos)
+{
+ umsdos_lookup_patch(dentry->d_parent->d_inode, dentry->d_inode, entry,
+ emd_pos);
+}
+
+
+struct UMSDOS_DIRENT_K {
+ off_t f_pos; /* will hold the offset of the entry in EMD */
+ ino_t ino;
+};
+
+
+/*
+ * Just to record the offset of one entry.
+ */
+
+static int umsdos_filldir_k ( void *buf,
+ const char *name,
+ int len,
+ off_t offset,
+ ino_t ino)
+{
+ struct UMSDOS_DIRENT_K *d = (struct UMSDOS_DIRENT_K *) buf;
+
+ d->f_pos = offset;
+ d->ino = ino;
+ return 0;
+}
+
+struct UMSDOS_DIR_SEARCH {
+ struct umsdos_dirent *entry;
+ int found;
+ ino_t search_ino;
+};
+
+static int umsdos_dir_search ( void *buf,
+ const char *name,
+ int len,
+ off_t offset,
+ ino_t ino)
+{
+ int ret = 0;
+ struct UMSDOS_DIR_SEARCH *d = (struct UMSDOS_DIR_SEARCH *) buf;
+
+ if (d->search_ino == ino) {
+ d->found = 1;
+ memcpy (d->entry->name, name, len);
+ d->entry->name[len] = '\0';
+ d->entry->name_len = len;
+ ret = 1; /* So fat_readdir will terminate */
+ }
+ return ret;
+}
+
+
+
+/*
+ * Locate the directory entry for a dentry in its parent 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_dentry_to_entry(struct dentry *dentry, struct umsdos_dirent *entry)
+{
+ struct dentry *parent = dentry->d_parent;
+ struct inode *inode = dentry->d_inode;
+ int ret = -ENOENT, err;
+ struct file filp;
+ struct UMSDOS_DIR_SEARCH bufsrch;
+ struct UMSDOS_DIRENT_K bufk;
+
+ if (pseudo_root && 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;
+ goto out;
+ }
+
+ /* initialize the file */
+ fill_new_filp (&filp, parent);
+
+ if (!umsdos_have_emd(parent)) {
+ /* This is a DOS directory. */
+ filp.f_pos = 0;
+ bufsrch.entry = entry;
+ bufsrch.search_ino = inode->i_ino;
+ fat_readdir (&filp, &bufsrch, umsdos_dir_search);
+ if (bufsrch.found) {
+ ret = 0;
+ inode->u.umsdos_i.i_dir_owner = parent->d_inode->i_ino;
+ inode->u.umsdos_i.i_emd_owner = 0;
+if (!S_ISDIR(inode->i_mode))
+printk("UMSDOS: %s/%s not a directory!\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+ /* N.B. why call this? not always a dir ... */
+ umsdos_setup_dir(dentry);
+ }
+ goto out;
+ }
+
+ /* skip . and .. see umsdos_readdir_x() */
+ filp.f_pos = UMSDOS_SPECIAL_DIRFPOS;
+ while (1) {
+ err = umsdos_readdir_x (parent->d_inode, &filp, &bufk, 1,
+ entry, 0, umsdos_filldir_k);
+ if (err < 0) {
+ printk ("UMSDOS: can't locate inode %ld in EMD??\n",
+ inode->i_ino);
+ break;
+ }
+ if (bufk.ino == inode->i_ino) {
+ ret = 0;
+ umsdos_lookup_patch_new(dentry, entry, bufk.f_pos);
+ break;
+ }
+ }
+out:
+ return ret;
+}
+
+/*
+ * Deprecated. Try to get rid of this soon!
+ */
+int umsdos_inode2entry (struct inode *dir, struct inode *inode,
+ struct umsdos_dirent *entry)
+{
+ int ret = -ENOENT;
+ struct inode *emddir;
+ struct dentry *i2e;
+ struct file filp;
+ struct UMSDOS_DIR_SEARCH bufsrch;
+ struct UMSDOS_DIRENT_K bufk;
+
+ if (pseudo_root && 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;
+ goto out;
+ }
+
+ emddir = umsdos_emd_dir_lookup (dir, 0);
+ if (emddir == NULL) {
+ /* This is a DOS directory. */
+ i2e = creat_dentry ("@i2e.nul@", 9, dir, NULL);
+ fill_new_filp (&filp, i2e);
+ filp.f_reada = 1;
+ filp.f_pos = 0;
+ bufsrch.entry = entry;
+ bufsrch.search_ino = inode->i_ino;
+ fat_readdir (&filp, &bufsrch, umsdos_dir_search);
+ if (bufsrch.found) {
+ ret = 0;
+ inode->u.umsdos_i.i_dir_owner = dir->i_ino;
+ inode->u.umsdos_i.i_emd_owner = 0;
+ umsdos_setup_dir_inode (inode);
+ }
+ goto out;
+ }
+
+ /* skip . and .. see umsdos_readdir_x() */
+
+ i2e = creat_dentry ("@i2e.nn@", 8, dir, NULL);
+ fill_new_filp (&filp, i2e);
+ filp.f_reada = 1;
+ filp.f_pos = UMSDOS_SPECIAL_DIRFPOS;
+ while (1) {
+ if (umsdos_readdir_x (dir, &filp, &bufk, 1,
+ entry, 0, umsdos_filldir_k) < 0) {
+ printk ("UMSDOS: can't locate inode %ld in EMD??\n",
+ inode->i_ino);
+ break;
+ }
+ if (bufk.ino == inode->i_ino) {
+ ret = 0;
+ umsdos_lookup_patch (dir, inode, entry, bufk.f_pos);
+ break;
+ }
+ }
+ iput (emddir);
+out:
+ return ret;
+}
+
+
+/*
+ * Return != 0 if an entry is the pseudo DOS entry in the pseudo root.
+ */
+
+int umsdos_is_pseudodos (struct inode *dir, struct dentry *dentry)
+{
+ /* #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
+ && dentry->d_name.len == 3
+ && dentry->d_name.name[0] == 'D'
+ && dentry->d_name.name[1] == 'O'
+ && dentry->d_name.name[2] == 'S';
+}
+
+
+/*
+ * Check whether a file exists in the current directory.
+ * Return 0 if OK, negative error code if not (ex: -ENOENT).
+ *
+ * fills dentry->d_inode with found inode, and increments its count.
+ * if not found, return -ENOENT.
+ */
+/* #Specification: umsdos / lookup
+ * A lookup for a file is done in two steps. 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.
+ */
+
+int umsdos_lookup_x (struct inode *dir, struct dentry *dentry, int nopseudo)
+{
+ const char *name = dentry->d_name.name;
+ int len = dentry->d_name.len;
+ struct dentry *dret = NULL;
+ struct inode *inode;
+ int ret = -ENOENT;
+ struct umsdos_info info;
+
+ umsdos_startlookup (dir);
+ /* this shouldn't happen ... */
+ if (len == 1 && name[0] == '.') {
+ printk("umsdos_lookup_x: UMSDOS broken, please report!\n");
+ goto out;
+ }
+
+ /* this shouldn't happen ... */
+ if (len == 2 && name[0] == '.' && name[1] == '.') {
+ printk("umsdos_lookup_x: UMSDOS broken, please report!\n");
+ goto out;
+ }
+
+ if (umsdos_is_pseudodos (dir, dentry)) {
+ /* #Specification: pseudo root / lookup(DOS)
+ * A lookup of DOS in the pseudo root will always succeed
+ * and return the inode of the real root.
+ */
+ inode = iget(dir->i_sb, UMSDOS_ROOT_INO);
+ if (inode)
+ goto out_add;
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
+ if (ret)
+ goto out;
+ ret = umsdos_findentry (dentry->d_parent, &info, 0);
+Printk (("lookup %.*s pos %lu ret %d len %d ",
+info.fake.len, info.fake.fname, info.f_pos, ret, info.fake.len));
+ if (ret)
+ goto out;
+
+
+ dret = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname,
+ info.fake.len);
+ ret = PTR_ERR(dret);
+ if (IS_ERR(dret))
+ goto out;
+ if (!dret->d_inode)
+ goto out_remove;
+
+ umsdos_lookup_patch_new(dret, &info.entry, info.f_pos);
+
+ /* Check for a hard link */
+ if (info.entry.flags & UMSDOS_HLINK) {
+Printk (("checking hard link %s/%s, ino=%ld, flags=%x\n",
+dret->d_parent->d_name.name, dret->d_name.name,
+dret->d_inode->i_ino, info.entry.flags));
+ dret = umsdos_solve_hlink (dret);
+ ret = PTR_ERR(dret);
+ if (IS_ERR(dret))
+ goto out;
+ }
+ /* N.B. can dentry be negative after resolving hlinks? */
+
+ if (pseudo_root && dret->d_inode == 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 links
+ * which are recorded independently of the pseudo-root
+ * mode.
+ */
+ Printk (("umsdos_lookup_x: untested Pseudo_root\n"));
+ ret = -ENOENT;
+ goto out_dput;
+ } else {
+ /* We've found it OK. Now put inode in dentry. */
+ inode = dret->d_inode;
+ }
+
+ /*
+ * Hash the dentry with the inode.
+ */
+out_add:
+ inode->i_count++;
+ d_add (dentry, inode);
+ ret = 0;
+
+out_dput:
+ dput(dret);
+out:
+ umsdos_endlookup (dir);
+ return ret;
+
+out_remove:
+ printk(KERN_WARNING "UMSDOS: entry %s/%s out of sync, erased\n",
+ dentry->d_name.name, info.fake.fname);
+ umsdos_delentry (dentry->d_parent, &info, S_ISDIR (info.entry.mode));
+ ret = -ENOENT;
+ goto out;
+}
+
+
+/*
+ * Check whether a file exists in the current directory.
+ * Return 0 if OK, negative error code if not (ex: -ENOENT).
+ *
+ * called by VFS. should fill dentry->d_inode (via d_add), and
+ * set (increment) dentry->d_inode->i_count.
+ *
+ */
+
+int UMSDOS_lookup (struct inode *dir, struct dentry *dentry)
+{
+ int ret;
+
+ ret = umsdos_lookup_x (dir, dentry, 0);
+
+ /* Create negative dentry if not found. */
+ if (ret == -ENOENT) {
+ Printk ((KERN_DEBUG
+ "UMSDOS_lookup: converting -ENOENT to negative\n"));
+ d_add (dentry, NULL);
+ ret = 0;
+ }
+ return ret;
+}
+
+
+/*
+ * Lookup or create a dentry from within the filesystem.
+ *
+ * We need to use this instead of lookup_dentry, as the
+ * directory semaphore lock is already held.
+ */
+struct dentry *umsdos_lookup_dentry(struct dentry *parent, char *name, int len)
+{
+ struct dentry *result, *dentry;
+ int error;
+ struct qstr qstr;
+
+ qstr.name = name;
+ qstr.len = len;
+ qstr.hash = full_name_hash(name, len);
+ result = d_lookup(parent, &qstr);
+ if (!result) {
+ result = ERR_PTR(-ENOMEM);
+ dentry = d_alloc(parent, &qstr);
+ if (dentry) {
+ result = dentry;
+ error = umsdos_real_lookup(parent->d_inode, result);
+ if (error)
+ goto out_fail;
+ }
+ }
+out:
+ return result;
+
+out_fail:
+ dput(result);
+ result = ERR_PTR(error);
+ goto out;
+}
+
+
+/*
+ * gets dentry which points to pseudo-hardlink
+ *
+ * it should try to find file it points to
+ * if file is found, it should dput() original dentry and return new one
+ * (with d_count = i_count = 1)
+ * Otherwise, it should return with error, with dput()ed original dentry.
+ *
+ */
+
+struct dentry *umsdos_solve_hlink (struct dentry *hlink)
+{
+ /* root is our root for resolving pseudo-hardlink */
+ struct dentry *base = hlink->d_sb->s_root;
+ struct dentry *final, *dir, *dentry_dst;
+ char *path, *pt;
+ unsigned long len;
+ int ret = -EIO;
+ struct file filp;
+
+ check_dentry_path (hlink, "HLINK BEGIN hlink");
+
+ final = ERR_PTR (-ENOMEM);
+ path = (char *) kmalloc (PATH_MAX, GFP_KERNEL);
+ if (path == NULL)
+ goto out;
+
+ fill_new_filp (&filp, hlink);
+ filp.f_flags = O_RDONLY;
+ filp.f_pos = 0;
+
+ Printk (("hlink2inode "));
+ len = umsdos_file_read_kmem (&filp, path, hlink->d_inode->i_size);
+ if (len != hlink->d_inode->i_size)
+ goto out_noread;
+
+ /* start at root dentry */
+ dir = dget(base);
+ path[hlink->d_inode->i_size] = '\0';
+ pt = path;
+ while (1) {
+ char *start = pt;
+ int len;
+
+ while (*pt != '\0' && *pt != '/') pt++;
+ len = (int) (pt - start);
+ if (*pt == '/') *pt++ = '\0';
+
+ dentry_dst = umsdos_lookup_dentry(dir, start, len);
+ if (IS_ERR(dentry_dst))
+ break;
+ if (dir->d_inode->u.umsdos_i.i_emd_dir == 0) {
+ /* This is a DOS directory */
+ ret = umsdos_rlookup_x (dir->d_inode, dentry_dst, 1);
+ } else {
+ ret = umsdos_lookup_x (dir->d_inode, dentry_dst, 1);
+ }
+ Printk ((" returned %d\n", ret));
+ dput (dir); /* dir no longer needed */
+ dir = dentry_dst;
+
+ Printk (("h2n lookup :%s: -> %d ", start, ret));
+ final = ERR_PTR (ret);
+ if (ret != 0) {
+ /* path component not found! */
+ break;
+ }
+ if (*pt == '\0') { /* we're finished! */
+ final = umsdos_lookup_dentry(hlink->d_parent,
+ (char *) hlink->d_name.name,
+ hlink->d_name.len);
+ break;
+ }
+ } /* end while */
+ /*
+ * See whether we found the path ...
+ */
+ if (!IS_ERR(final)) {
+ if (!final->d_inode) {
+ d_instantiate(final, dir->d_inode);
+ /* we need inode to survive. */
+ dir->d_inode->i_count++;
+ } else {
+ printk ("umsdos_solve_hlink: %s/%s already exists\n",
+ final->d_parent->d_name.name,
+ final->d_name.name);
+ }
+printk ("umsdos_solve_hlink: ret = %d, %s/%s -> %s/%s\n",
+ret, hlink->d_parent->d_name.name, hlink->d_name.name,
+final->d_parent->d_name.name, final->d_name.name);
+ }
+ dput(dir);
+
+out_free:
+ kfree (path);
+
+out:
+ dput(hlink); /* original hlink no longer needed */
+ check_dentry_path (hlink, "HLINK FIN hlink");
+ check_dentry_path (final, "HLINK RET final");
+ return final;
+
+out_noread:
+ printk("umsdos_solve_hlink: failed reading pseudolink!\n");
+ goto out_free;
+}
+
+
+static struct file_operations umsdos_dir_operations =
+{
+ NULL, /* lseek - default */
+ dummy_dir_read, /* read */
+ NULL, /* write - bad */
+ UMSDOS_readdir, /* readdir */
+ NULL, /* poll - default */
+ UMSDOS_ioctl_dir, /* ioctl - default */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* flush */
+ 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, /* followlink */
+ generic_readpage, /* readpage */
+ NULL, /* writepage */
+ fat_bmap, /* bmap */
+ NULL, /* truncate */
+ NULL, /* permission */
+ NULL, /* smap */
+ NULL, /* updatepage */
+ NULL, /* revalidate */
+
+};
@@ -129,27+129,22 @@ ssize_t umsdos_file_write_kmem (struct file *filp,
Printk ((KERN_DEBUG " STARTED WRITE_KMEM /mn/\n"));
ret = umsdos_file_write_kmem_real (filp, buf, count);
-
-#warning Should d_drop be here ?
-#if 0
- d_drop (filp->f_dentry);
-#endif
-
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.
+ *
+ * Note: buffer is in kernel memory, not in user space.
*/
ssize_t umsdos_emd_dir_write ( struct file *filp,
- char *buf, /* buffer in kernel memory, not in user space */
+ char *buf,
size_t count)
{
int written;
@@ -168,7+163,8 @@ ssize_t umsdos_emd_dir_write ( struct file *filp, #endif
filp->f_flags = 0;
- Printk (("umsdos_emd_dir_write /mn/: calling write_kmem with %p, %p, %d, %Ld\n", filp, buf, count, filp->f_pos));
+Printk (("umsdos_emd_dir_write /mn/: calling write_kmem with %p, %p, %d, %Ld\n",
+filp, buf, count, filp->f_pos));
written = umsdos_file_write_kmem (filp, buf, count);
Printk (("umsdos_emd_dir_write /mn/: write_kmem returned\n"));
@@ -184,11+180,11 @@ ssize_t umsdos_emd_dir_write ( struct file *filp, #endif
#if UMS_DEBUG
- if (written != count)
- Printk ((KERN_ERR "umsdos_emd_dir_write: ERROR: written (%d) != count (%d)\n", written, count));
+if (written != count)
+printk(KERN_ERR "umsdos_emd_dir_write: ERROR: written (%d) != count (%d)\n",
+written, count);
#endif
-
return written != count ? -EIO : 0;
}
@@ -199,10+195,9 @@ ssize_t umsdos_emd_dir_write ( struct file *filp, * The block of data is NOT in user space.
* Return 0 if OK, -EIO if any error.
*/
+/* buffer in kernel memory, not in user space */
-ssize_t umsdos_emd_dir_read (struct file *filp,
- char *buf, /* buffer in kernel memory, not in user space */
- size_t count)
+ssize_t umsdos_emd_dir_read (struct file *filp, char *buf, size_t count)
{
long int ret = 0;
int sizeread;
@@ -216,7+211,8 @@ ssize_t umsdos_emd_dir_read (struct file *filp, filp->f_flags = 0;
sizeread = umsdos_file_read_kmem (filp, buf, count);
if (sizeread != count) {
- printk ("UMSDOS: problem with EMD file: can't read pos = %Ld (%d != %d)\n", filp->f_pos, sizeread, count);
+printk ("UMSDOS: problem with EMD file: can't read pos = %Ld (%d != %d)\n",
+filp->f_pos, sizeread, count);
ret = -EIO;
}
#ifdef __BIG_ENDIAN
@@ -234,40+230,72 @@ ssize_t umsdos_emd_dir_read (struct file *filp, }
-
/*
- * this checks weather filp points to directory or file,
- * and if directory, it assumes that it has not yet been
- * converted to point to EMD_FILE, and fixes it
- *
- * calling code should save old filp->f_dentry, call fix_emd_filp
- * and if it succeeds (return code 0), do fin_dentry (filp->f_dentry)
- * when it is over. It should also restore old filp->f_dentry.
- *
+ * Create the EMD dentry for a directory.
*/
+struct dentry *umsdos_get_emd_dentry(struct dentry *parent)
+{
+ struct dentry *demd;
+
+ demd = umsdos_lookup_dentry (parent, UMSDOS_EMD_FILE,
+ UMSDOS_EMD_NAMELEN);
+ return demd;
+}
-int fix_emd_filp (struct file *filp)
+/*
+ * Check whether a directory has an EMD file.
+ */
+int umsdos_have_emd(struct dentry *dir)
{
- struct inode *dir=filp->f_dentry->d_inode;
- struct inode *emd_dir;
-
- /* is current file (which should be EMD or directory) EMD? */
- if (dir->u.umsdos_i.i_emd_owner == 0xffffffff) {
- dget (filp->f_dentry);
- Printk ((KERN_WARNING "\nfix_emd_filp: EMD already done (should not be !)\n\n"));
- return 0;
+ struct dentry *demd = umsdos_get_emd_dentry (dir);
+ int found = 0;
+
+ if (!IS_ERR(demd)) {
+ if (demd->d_inode)
+ found = 1;
+ dput(demd);
}
- /* it is not, we need to make it so */
-
- emd_dir = umsdos_emd_dir_lookup (dir, 0);
- if (emd_dir == NULL) {
- Printk ((KERN_ERR "\nfix_emd_filp: EMD not found (should never happen)!!!\n\n"));
- return -99;
+ return found;
+}
+
+/*
+ * Create the EMD file for a directory if it doesn't
+ * already exist. Returns 0 or and error code.
+ */
+int umsdos_make_emd(struct dentry *parent)
+{
+ struct dentry *demd = umsdos_get_emd_dentry(parent);
+ struct inode *inode;
+ int err = PTR_ERR(demd);
+
+ if (IS_ERR(demd))
+ goto out;
+
+ /* already created? */
+ inode = demd->d_inode;
+ if (inode) {
+ parent->d_inode->u.umsdos_i.i_emd_dir = inode->i_ino;
+ err = 0;
+ goto out_dput;
}
-
- filp->f_dentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, emd_dir, filp->f_dentry); /* filp->f_dentry is dir containing EMD file, so it IS the parent dentry... */
- return 0;
+printk("umsdos_make_emd: creating %s/%s\n",
+parent->d_name.name, demd->d_name.name);
+
+ err = msdos_create(parent->d_inode, demd, S_IFREG | 0777);
+ if (err) {
+ printk (KERN_WARNING "UMSDOS: Can't create EMD file\n");
+ goto out_dput;
+ }
+ inode = demd->d_inode;
+ parent->d_inode->u.umsdos_i.i_emd_dir = inode->i_ino;
+ /* Disable UMSDOS_notify_change() for EMD file */
+ inode->u.umsdos_i.i_emd_owner = 0xffffffff;
+
+out_dput:
+ dput(demd);
+out:
+ return err;
}
@@ -275,7+303,8 @@ int fix_emd_filp (struct file *filp) * Locate the EMD file in a directory.
*
* Return NULL if error, dir->u.umsdos_i.emd_inode if OK.
- * caller must iput() returned inode when finished with it!
+ * Caller must iput() returned inode when finished with it!
+ * Note: deprecated; get rid of this soon!
*/
struct inode *umsdos_emd_dir_lookup (struct inode *dir, int creat)
@@ -285,102+314,77 @@ struct inode *umsdos_emd_dir_lookup (struct inode *dir, int creat) int rv;
Printk ((KERN_DEBUG "Entering umsdos_emd_dir_lookup\n"));
- if (!dir) printk (KERN_CRIT "umsdos FATAL: should never happen: dir=NULL!\n");
+ if (!dir) {
+ printk (KERN_CRIT "umsdos_emd_dir_lookup: FATAL, dir=NULL!\n");
+ goto out;
+ }
check_inode (dir);
if (dir->u.umsdos_i.i_emd_dir != 0) {
ret = iget (dir->i_sb, dir->u.umsdos_i.i_emd_dir);
- Printk (("umsdos_emd_dir_lookup: deja trouve %ld %p\n", dir->u.umsdos_i.i_emd_dir, ret));
- } else {
- PRINTK ((KERN_DEBUG "umsdos /mn/: Looking for %.*s -", UMSDOS_EMD_NAMELEN, UMSDOS_EMD_FILE));
-
- d_dir = geti_dentry (dir);
- dlook = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL, d_dir);
- rv = umsdos_real_lookup (dir, dlook);
-
- PRINTK ((KERN_DEBUG "-returned %d\n", rv));
- Printk ((KERN_INFO "emd_dir_lookup "));
-
- ret = dlook->d_inode;
- if (ret) {
- Printk (("Found --linux "));
- dir->u.umsdos_i.i_emd_dir = ret->i_ino;
- inc_count (ret); /* we'll need the inode */
- fin_dentry (dlook); /* but not dentry */
- check_inode (ret);
- } else if (creat) {
- int code;
-
- Printk ((" * ERROR * /mn/: creat not yet implemented? not fixed? "));
- Printk (("avant create "));
- inc_count (dir);
-
- check_inode (ret);
- code = compat_msdos_create (dir, UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, S_IFREG | 0777, &ret);
- check_inode (ret);
- Printk (("Creat EMD code %d ret %p ", code, ret));
- if (ret != NULL) {
- Printk ((" ino=%lu", ret->i_ino));
- dir->u.umsdos_i.i_emd_dir = ret->i_ino;
- } else {
- printk (KERN_WARNING "UMSDOS: Can't create EMD file\n");
- }
- }
-
- if (ret != NULL) {
- /* Disable UMSDOS_notify_change() for EMD file */
- /* inc_count (ret); // we need to return with incremented inode. FIXME: didn't umsdos_real_lookup already did that? and compat_msdos_create ? */
- ret->u.umsdos_i.i_emd_owner = 0xffffffff;
- }
+ Printk (("umsdos_emd_dir_lookup: deja trouve %ld %p\n",
+ dir->u.umsdos_i.i_emd_dir, ret));
+ goto out;
}
-#if UMS_DEBUG
- Printk ((KERN_DEBUG "umsdos_emd_dir_lookup returning %p /mn/\n", ret));
- if (ret != NULL)
- Printk ((KERN_DEBUG " returning ino=%lu\n", ret->i_ino));
-#endif
- return ret;
-}
-
-
-
-/*
- * creates an EMD file
- *
- * Return NULL if error, dir->u.umsdos_i.emd_inode if OK.
- */
-
-struct inode *umsdos_emd_dir_create (struct inode *dir, struct dentry *dentry, int mode)
-{
- 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 %lu %p", dir->u.umsdos_i.i_emd_dir, ret));
- } else {
+ PRINTK ((KERN_DEBUG "umsdos /mn/: Looking for %.*s -",
+ UMSDOS_EMD_NAMELEN, UMSDOS_EMD_FILE));
+ d_dir = geti_dentry (dir);
+ if (!d_dir) {
+printk("UMSDOS: flaky i_dentry hack failed\n");
+ goto out;
+ }
+ dlook = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL, d_dir);
+ if (!dlook)
+ goto out;
+ rv = umsdos_real_lookup (dir, dlook);
+
+ PRINTK ((KERN_DEBUG "-returned %d\n", rv));
+ Printk ((KERN_INFO "emd_dir_lookup "));
+
+ ret = dlook->d_inode;
+ if (ret) {
+ Printk (("Found --linux "));
+ dir->u.umsdos_i.i_emd_dir = ret->i_ino;
+ ret->i_count++; /* we'll need the inode */
+ check_inode (ret);
+ } else if (creat) {
int code;
-
+
+ Printk ((" * ERROR * /mn/: creat not yet implemented? not fixed? "));
Printk (("avant create "));
- inc_count (dir);
- code = compat_msdos_create (dir, UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, S_IFREG | 0777, &ret);
+
+ check_inode (ret);
+ code = compat_msdos_create (dir, UMSDOS_EMD_FILE,
+ UMSDOS_EMD_NAMELEN,
+ S_IFREG | 0777, &ret);
+ check_inode (ret);
Printk (("Creat EMD code %d ret %p ", code, ret));
if (ret != NULL) {
+ Printk ((" ino=%lu", ret->i_ino));
dir->u.umsdos_i.i_emd_dir = ret->i_ino;
} else {
- printk ("UMSDOS: Can't create EMD file\n");
+ printk (KERN_WARNING "UMSDOS: Can't create EMD file\n");
}
}
-
+ dput(dlook);
+
if (ret != NULL) {
/* Disable UMSDOS_notify_change() for EMD file */
ret->u.umsdos_i.i_emd_owner = 0xffffffff;
}
+
+out:
+#if UMS_DEBUG
+ Printk ((KERN_DEBUG "umsdos_emd_dir_lookup returning %p /mn/\n", ret));
+ if (ret != NULL)
+ Printk ((KERN_DEBUG " returning ino=%lu\n", ret->i_ino));
+#endif
return ret;
}
-
/*
* Read an entry from the EMD file.
* Support variable length record.
@@ -389,13+393,11 @@ struct inode *umsdos_emd_dir_create (struct inode *dir, struct dentry *dentry, i * does not change {d,i}_count
*/
-int umsdos_emd_dir_readentry ( struct file *filp,
- struct umsdos_dirent *entry)
+int umsdos_emd_dir_readentry (struct file *filp, struct umsdos_dirent *entry)
{
int ret;
Printk ((KERN_DEBUG "umsdos_emd_dir_readentry /mn/: entering.\n"));
- Printk (("umsdos_emd_dir_readentry /mn/: reading EMD %.*s (ino=%lu) at pos=%d\n", (int) filp->f_dentry->d_name.len, filp->f_dentry->d_name.name, filp->f_dentry->d_inode->i_ino, (int) filp->f_pos));
ret = umsdos_emd_dir_read (filp, (char *) entry, UMSDOS_REC_SIZE);
if (ret == 0) { /* if no error */
@@ -403,41+405,48 @@ int umsdos_emd_dir_readentry ( struct file *filp, int recsize = umsdos_evalrecsize (entry->name_len);
if (recsize > UMSDOS_REC_SIZE) {
- Printk ((KERN_DEBUG "umsdos_emd_dir_readentry /mn/: %d > %d!\n", recsize, UMSDOS_REC_SIZE));
- ret = umsdos_emd_dir_read (filp, ((char *) entry) + UMSDOS_REC_SIZE, recsize - UMSDOS_REC_SIZE);
+Printk ((KERN_DEBUG "umsdos_emd_dir_readentry /mn/: %d > %d!\n",
+recsize, UMSDOS_REC_SIZE));
+ ret = umsdos_emd_dir_read (filp,
+ ((char *) entry) + UMSDOS_REC_SIZE,
+ recsize - UMSDOS_REC_SIZE);
}
}
Printk (("umsdos_emd_dir_readentry /mn/: ret=%d.\n", ret));
if (entry && ret == 0) {
- Printk (("umsdos_emd_dir_readentry /mn/: returning len=%d,name=%.*s\n", (int) entry->name_len, (int) entry->name_len, entry->name));
+Printk (("umsdos_emd_dir_readentry /mn/: returning len=%d,name=%.*s\n",
+(int) entry->name_len, (int) entry->name_len, entry->name));
}
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 dentry *emd_dentry;
- struct file filp;
+static int umsdos_writeentry (struct dentry *parent, struct umsdos_info *info,
+ int free_entry)
+{
+ struct inode *dir = parent->d_inode;
struct umsdos_dirent *entry = &info->entry;
+ struct dentry *emd_dentry;
+ int ret;
struct umsdos_dirent entry0;
+ struct file filp;
- fill_new_filp (&filp, NULL);
-
- Printk (("umsdos_writeentry /mn/: entering...\n"));
- emd_dentry = geti_dentry (emd_dir);
+ emd_dentry = umsdos_get_emd_dentry(parent);
+ ret = PTR_ERR(emd_dentry);
+ if (IS_ERR(emd_dentry))
+ goto out;
+ /* make sure there's an EMD file */
+ ret = -EIO;
+ if (!emd_dentry->d_inode) {
+printk("umsdos_writeentry: no EMD file in %s/%s\n",
+parent->d_parent->d_name.name, parent->d_name.name);
+ goto out_dput;
+ }
if (free_entry) {
/* #Specification: EMD file / empty entries
@@ -449,7+458,8 @@ int umsdos_writeentry ( struct inode *dir, 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);
+ 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
@@ -458,25+468,23 @@ int umsdos_writeentry ( struct inode *dir, */
memset (entry->spare, 0, sizeof (entry->spare));
}
- Printk (("umsdos_writeentry /mn/: if passed...\n"));
- if (!info)
- printk (KERN_ERR "UMSDOS: /mn/ info is empty! Oops!\n");
+ fill_new_filp (&filp, emd_dentry);
filp.f_pos = info->f_pos;
filp.f_reada = 0;
filp.f_flags = O_RDWR;
- filp.f_dentry = emd_dentry;
- filp.f_op = &umsdos_file_operations; /* /mn/ - We have to fill it with dummy values so we won't segfault. */
+ /* write the entry and update the parent timestamps */
ret = umsdos_emd_dir_write (&filp, (char *) entry, info->recsize);
- Printk (("emd_dir_write returned with %d!\n", ret));
- if (ret != 0) {
- printk ("UMSDOS: problem with EMD file: can't write\n");
- } else {
+ if (!ret) {
dir->i_ctime = dir->i_mtime = CURRENT_TIME;
- /* dir->i_dirt = 1; FIXME iput/dput ??? */
- }
+ mark_inode_dirty(dir);
+ } else
+ printk ("UMSDOS: problem with EMD file: can't write\n");
+out_dput:
+ dput(emd_dentry);
+out:
Printk (("umsdos_writeentry /mn/: returning %d...\n", ret));
return ret;
}
@@ -493,26+501,19 @@ struct find_buffer {
-
-
/*
- * Fill the read buffer and take care of the byte remaining inside.
- * Unread bytes are simply move to the beginning.
+ * Fill the read buffer and take care of the bytes remaining inside.
+ * Unread bytes are simply moved to the beginning.
*
* Return -ENOENT if EOF, 0 if OK, a negative error code if any problem.
*/
-static int umsdos_fillbuf (
- struct inode *inode,
- struct find_buffer *buf)
+static int umsdos_fillbuf (struct find_buffer *buf)
{
- int ret = -ENOENT;
+ struct inode *inode = buf->filp.f_dentry->d_inode;
int mustmove = buf->size - buf->pos;
- int mustread;
- int remain;
- struct inode *old_ino;
-
- PRINTK ((KERN_DEBUG "Entering umsdos_fillbuf, for inode %lu, buf=%p\n", inode->i_ino, buf));
+ int mustread, remain;
+ int ret = -ENOENT;
if (mustmove > 0) {
memcpy (buf->buffer, buf->buffer + buf->pos, mustmove);
@@ -523,10+524,8 @@ static int umsdos_fillbuf ( if (remain < mustread)
mustread = remain;
if (mustread > 0) {
- old_ino = buf->filp.f_dentry->d_inode; /* FIXME: do we need to save/restore it ? */
- buf->filp.f_dentry->d_inode = inode;
- ret = umsdos_emd_dir_read (&buf->filp, buf->buffer + mustmove, mustread);
- buf->filp.f_dentry->d_inode = old_ino;
+ ret = umsdos_emd_dir_read (&buf->filp, buf->buffer + mustmove,
+ mustread);
if (ret == 0)
buf->size = mustmove + mustread;
} else if (mustmove) {
@@ -543,8+542,6 @@ static int umsdos_fillbuf ( * 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.
*
@@ -556,144+553,121 @@ static int umsdos_fillbuf ( * 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.
- *
- * calling code is expected to iput() returned *pt_emd_dir
+ * All this to say that umsdos_writeentry must be called after this
+ * function since it relies on the f_pos field of info.
*
*/
+/* #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 in a single
+ * record, multiple contiguous records are allocated.
+ */
-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 */
-
+static int umsdos_find (struct dentry *parent, struct umsdos_info *info)
{
- /* #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 in a single
- * record, multiple contiguous records are allocated.
- */
- int ret = -ENOENT;
- struct inode *emd_dir;
struct umsdos_dirent *entry = &info->entry;
-
- Printk (("umsdos_find: locating %.*s in dir %lu\n", entry->name_len, entry->name, dir->i_ino));
- check_inode (dir);
-
- emd_dir = umsdos_emd_dir_lookup (dir, 1);
- if (emd_dir != NULL) {
- 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;
- struct dentry *demd;
-
- Printk (("umsdos_find: check emd_dir...\n"));
- check_inode (emd_dir);
-
-#if 0 /* FIXME! not needed. but there are count wraps. somewhere before umsdos_find there should be inc_count/iput pair around umsdos_find call.... */
- inc_count (emd_dir); /* since we are going to fin_dentry, and need emd_dir afterwards -- caling code will iput() it */
-#endif
- demd = geti_dentry (emd_dir);
- if (demd) {
- dget (demd); /* because we'll have to dput it */
+ int recsize = info->recsize;
+ struct dentry *demd;
+ struct inode *emd_dir;
+ int ret = -ENOENT;
+ struct find_buffer buf;
+ 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;
+
+Printk (("umsdos_find: locating %s in %s/%s\n",
+entry->name, parent->d_parent->d_name.name, parent->d_name.name));
+
+ /*
+ * Lookup the EMD file in the parent directory.
+ */
+ demd = umsdos_get_emd_dentry(parent);
+ ret = PTR_ERR(demd);
+ if (IS_ERR(demd))
+ goto out;
+ /* make sure there's an EMD file ... */
+ ret = -ENOENT;
+ emd_dir = demd->d_inode;
+ if (!emd_dir)
+ goto out_dput;
+
+Printk(("umsdos_find: found EMD file %s/%s, ino=%p\n",
+demd->d_parent->d_name.name, demd->d_name.name, emd_dir));
+
+ fill_new_filp (&buf.filp, demd);
+
+ buf.pos = 0;
+ buf.size = 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 (&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 */
+ /* as large as recsize. */
+ 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 {
- /*
- * We don't have dentry alias for this inode. Too bad.
- * So we'll fake something (as best as we can).
- * (maybe we should do it in any case just to keep it simple?)
- *
- * Note that this is legal for EMD file, since in some places
- * we keep inode, but discard dentry (since we would have no way
- * to discard it later). Yes, this probably should be fixed somehow,
- * it is just that I don't have idea how right now, and I've spent
- * quite some time to track it down why it dies here. Maybe new emd_dir_lookup
- * which returns dentry ? hmmmm... FIXME...
- *
- */
- Printk ((KERN_WARNING "umsdos_find: inode has no alias for EMD inode, fake it\n"));
- demd = creat_dentry ("@emd_find@", 10, emd_dir, NULL);
- }
-
- check_dentry_path (demd, " EMD_DIR_DENTRY umsdos_find");
-
- fill_new_filp (&buf.filp, demd);
+ int entry_size = umsdos_evalrecsize (rentry->name_len);
- buf.pos = 0;
- buf.size = 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 (buf.pos + entry_size > buf.size) {
+ ret = umsdos_fillbuf (&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 */
- /* as large as recsize. */
- if (entry->name_len == 0) {
+ } 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 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;
- }
+ buf.pos += entry_size;
}
}
}
- umsdos_manglename (info);
- fin_dentry (demd);
}
- *pt_emd_dir = emd_dir;
+ umsdos_manglename (info);
+out_dput:
+ dput(demd);
+
+out:
Printk (("umsdos_find: returning %d\n", ret));
return ret;
}
@@ -703,23+677,21 @@ static int umsdos_find ( struct inode *dir, * Add a new entry in the EMD file.
* Return 0 if OK or a negative error code.
* Return -EEXIST if the entry already exists.
- *
+ *
* Complete the information missing in info.
+ *
+ * N.B. What if the EMD file doesn't exist?
*/
-int umsdos_newentry ( struct inode *dir,
- struct umsdos_info *info)
+int umsdos_newentry (struct dentry *parent, struct umsdos_info *info)
{
- struct inode *emd_dir;
- int ret = umsdos_find (dir, info, &emd_dir);
+ int err, ret = -EEXIST;
- if (ret == 0) {
- ret = -EEXIST;
- } else if (ret == -ENOENT) {
- ret = umsdos_writeentry (dir, emd_dir, info, 0);
+ err = umsdos_find (parent, info);
+ if (err && err == -ENOENT) {
+ ret = umsdos_writeentry (parent, info, 0);
Printk (("umsdos_writeentry EMD ret = %d\n", ret));
}
- iput (emd_dir);
return ret;
}
@@ -729,27+701,27 @@ int umsdos_newentry ( struct inode *dir, * Return 0 if OK, an error code if not.
*/
-int umsdos_newhidden ( struct inode *dir,
- struct umsdos_info *info)
+/* #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.
+ */
+int umsdos_newhidden (struct dentry *parent, 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);
+ ret = umsdos_find (parent, info);
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);
+ 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.
@@ -757,72+729,75 @@ int umsdos_newhidden ( struct inode *dir, * Complete the information missing in info.
*/
-int umsdos_delentry ( struct inode *dir,
- struct umsdos_info *info,
- int isdir)
+int umsdos_delentry (struct dentry *parent, struct umsdos_info *info, int isdir)
{
- struct inode *emd_dir;
- int ret = umsdos_find (dir, info, &emd_dir);
+ int ret;
- 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);
- }
+ ret = umsdos_find (parent, info);
+ if (ret)
+ goto out;
+ if (info->entry.name_len == 0)
+ goto out;
+
+ if ((isdir != 0) != (S_ISDIR (info->entry.mode) != 0)) {
+ if (S_ISDIR (info->entry.mode)) {
+ ret = -EISDIR;
+ } else {
+ ret = -ENOTDIR;
}
+ goto out;
}
- iput (emd_dir);
+ ret = umsdos_writeentry (parent, info, 1);
+
+out:
return ret;
}
/*
- * Verify that a EMD directory is empty. Return
+ * Verify that an EMD directory is empty.
+ * Return:
* 0 if not empty,
- * 1 if empty,
+ * 1 if empty (except for EMD file),
* 2 if empty or no EMD file.
*/
-int umsdos_isempty (struct inode *dir)
+int umsdos_isempty (struct dentry *dentry)
{
- struct dentry *dentry, *d_dir;
-
+ struct dentry *demd;
int ret = 2;
- struct inode *emd_dir = umsdos_emd_dir_lookup (dir, 0);
+ struct file filp;
+ demd = umsdos_get_emd_dentry(dentry);
+ if (IS_ERR(demd))
+ goto out;
/* If the EMD file does not exist, it is certainly empty. :-) */
- if (emd_dir != NULL) {
- struct file filp;
+ if (!demd->d_inode)
+ goto out_dput;
- d_dir = geti_dentry (dir);
- dentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, emd_dir, d_dir);
- check_dentry_path (dentry, "umsdos_isempty BEGIN");
- fill_new_filp (&filp, dentry);
- filp.f_pos = 0;
- filp.f_flags = O_RDONLY;
+ fill_new_filp (&filp, demd);
+ filp.f_flags = O_RDONLY;
- ret = 1;
- while (filp.f_pos < emd_dir->i_size) {
- struct umsdos_dirent entry;
+ ret = 1;
+ while (filp.f_pos < demd->d_inode->i_size) {
+ struct umsdos_dirent entry;
- if (umsdos_emd_dir_readentry (&filp, &entry) != 0) {
- ret = 0;
- break;
- } else if (entry.name_len != 0) {
- ret = 0;
- break;
- }
+ if (umsdos_emd_dir_readentry (&filp, &entry) != 0) {
+ ret = 0;
+ break;
+ }
+ if (entry.name_len != 0) {
+ ret = 0;
+ break;
}
- fin_dentry (dentry);
- check_dentry_path (dentry, "umsdos_isempty END");
- iput (emd_dir);
}
+
+out_dput:
+ dput(demd);
+out:
+printk("umsdos_isempty: checked %s/%s, empty=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, ret);
+
return ret;
}
@@ -832,28+807,28 @@ int umsdos_isempty (struct inode *dir) *
* does not change i_count
*/
+/* 0: anything */
+/* 1: file */
+/* 2: directory */
-int umsdos_findentry ( struct inode *dir,
- struct umsdos_info *info,
- int expect)
- /* 0: anything */
- /* 1: file */
- /* 2: directory */
+int umsdos_findentry (struct dentry *parent, struct umsdos_info *info,
+ int expect)
{
- struct inode *emd_dir=NULL;
- 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;
- }
+ int ret;
+
+ ret = umsdos_find (parent, info);
+ if (ret)
+ goto out;
+
+ if (expect != 0) {
+ if (S_ISDIR (info->entry.mode)) {
+ if (expect != 2)
+ ret = -EISDIR;
+ } else if (expect == 2) {
+ ret = -ENOTDIR;
}
}
- iput (emd_dir);
+out:
Printk (("umsdos_findentry: returning %d\n", ret));
return ret;
}
-/*
- * linux/fs/umsdos/inode.c
- *
- * Written 1993 by Jacques Gelinas
- * Inspired from linux/fs/msdos/... by Werner Almesberger
- */
-
-#include <linux/module.h>
-
-#include <linux/init.h>
-#include <linux/fs.h>
-#include <linux/msdos_fs.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/errno.h>
-#include <asm/uaccess.h>
-#include <linux/string.h>
-#include <linux/stat.h>
-#include <linux/umsdos_fs.h>
-#include <linux/list.h>
-
-extern struct inode_operations umsdos_rdir_inode_operations;
-
-
-struct inode *pseudo_root = NULL; /* Useful to simulate the pseudo DOS */
- /* directory. See UMSDOS_readdir_x() */
-
-
-/*
- * returns inode->i_dentry
- *
- */
-
-inline struct dentry *geti_dentry (struct inode *inode)
-{
- struct dentry *ret;
- if (!inode) {
- printk (KERN_ERR "geti_dentry: ERROR: inode is NULL!\n");
- return NULL;
- }
- if (inode->i_dentry.next == inode->i_dentry.next->next) {
- printk (KERN_WARNING "geti_dentry: WARNING: inode does not have an dentry. returning NULL.\n");
- return NULL;
- }
- ret = list_entry (inode->i_dentry.next, struct dentry, d_alias);
-
- if (IS_ERR(ret)) {
- Printk ((KERN_WARNING "geti_dentry: checking dentry... it is ERR(%ld) !\n", PTR_ERR(ret)));
- }
-
- PRINTK ((KERN_DEBUG "geti_dentry : inode %lu: i_dentry is %p\n", inode->i_ino, ret));
- return ret;
-}
-
-
-
-/*
- * makes inode->i_count++
- *
- */
-
-inline void inc_count (struct inode *inode)
-{
- inode->i_count++;
- PRINTK ((KERN_DEBUG "inc_count: inode %lu incremented count to %d\n", inode->i_ino, inode->i_count));
-}
-
-
-/*
- * makes empty filp
- *
- */
-
-void fill_new_filp (struct file *filp, struct dentry *dentry)
-{
- Printk (("/mn/ fill_new_filp: filling empty filp at %p\n", filp));
- if (dentry)
- Printk ((" dentry=%.*s\n", (int) dentry->d_name.len, dentry->d_name.name));
- else
- Printk ((" dentry is NULL ! you must fill it later...\n"));
-
- memset (filp, 0, sizeof (struct file));
-
- filp->f_pos = 0;
- filp->f_reada = 1;
- filp->f_flags = O_RDWR;
- filp->f_dentry = dentry;
- filp->f_op = &umsdos_file_operations; /* /mn/ - we have to fill it with SOMETHING */
-}
-
-
-
-#if UMS_DEBUG
-/*
- * check a superblock
- */
-
-void check_sb (struct super_block *sb, const char c)
-{
- if (sb) {
- Printk ((" (has %c_sb=%d, %d)", c, MAJOR (sb->s_dev), MINOR (sb->s_dev)));
- } else {
- Printk ((" (%c_sb is NULL)", c));
- }
-}
-
-/*
- * check an inode
- */
-
-void check_inode (struct inode *inode)
-{
- if (inode) {
- Printk ((KERN_DEBUG "* inode is %lu (i_count=%d)", inode->i_ino, inode->i_count));
- check_sb (inode->i_sb, 'i');
-
- if (inode->i_dentry.next) { /* FIXME: does this work ? */
- Printk ((" (has i_dentry)"));
- } else {
- Printk ((" (NO i_dentry)"));
- }
-
- if (inode->i_op == NULL) {
- Printk ((" (i_op is NULL)\n"));
- } else if (inode->i_op == &umsdos_dir_inode_operations) {
- Printk ((" (i_op is umsdos_dir_inode_operations)\n"));
- } else if (inode->i_op == &umsdos_file_inode_operations) {
- Printk ((" (i_op is umsdos_file_inode_operations)\n"));
- } else if (inode->i_op == &umsdos_file_inode_operations_no_bmap) {
- Printk ((" (i_op is umsdos_file_inode_operations_no_bmap)\n"));
- } else if (inode->i_op == &umsdos_file_inode_operations_readpage) {
- Printk ((" (i_op is umsdos_file_inode_operations_readpage)\n"));
- } else if (inode->i_op == &umsdos_rdir_inode_operations) {
- Printk ((" (i_op is umsdos_rdir_inode_operations)\n"));
- } else if (inode->i_op == &umsdos_symlink_inode_operations) {
- Printk ((" (i_op is umsdos_symlink_inode_operations)\n"));
- } else {
- Printk ((" (i_op is UNKNOWN: %p)\n", inode->i_op));
- }
- } else {
- Printk ((KERN_DEBUG "* inode is NULL\n"));
- }
-}
-
-/*
- * checks all inode->i_dentry
- *
- */
-void checkd_inode (struct inode *inode)
-{
- struct dentry *ret;
- struct list_head *cur;
- int count = 0;
- if (!inode) {
- printk (KERN_ERR "checkd_inode: inode is NULL!\n");
- return;
- }
-
- Printk ((KERN_DEBUG "checkd_inode: inode %lu\n", inode->i_ino));
- cur = inode->i_dentry.next;
- while (count++ < 10) {
- PRINTK (("1..."));
- if (!cur) {
- Printk ((KERN_ERR "checkd_inode: *** NULL reached. exit.\n"));
- return;
- }
- PRINTK (("2..."));
- ret = list_entry (cur, struct dentry, d_alias);
- PRINTK (("3..."));
- if (cur == cur->next) {
- Printk ((KERN_DEBUG "checkd_inode: *** cur=cur->next: normal exit.\n"));
- return;
- }
- PRINTK (("4..."));
- if (!ret) {
- Printk ((KERN_ERR "checkd_inode: *** ret dentry is NULL. exit.\n"));
- return;
- }
- PRINTK (("5... (ret=%p)...", ret));
- PRINTK (("5.1.. (ret->d_dname=%p)...", &(ret->d_name)));
- PRINTK (("5.1.1. (ret->d_dname.len=%d)...", (int) ret->d_name.len));
- PRINTK (("5.1.2. (ret->d_dname.name=%c)...", ret->d_name.name));
- Printk ((KERN_DEBUG "checkd_inode: i_dentry is %.*s\n", (int) ret->d_name.len, ret->d_name.name));
- PRINTK (("6..."));
- cur = cur->next;
- PRINTK (("7..."));
-#if 1
- Printk ((KERN_DEBUG "checkd_inode: *** finished after count 1 (operator forced)\n"));
- return;
-#endif
- }
- Printk ((KERN_ERR "checkd_inode: *** OVER LIMIT (loop?) !\n"));
- return;
-}
-
-/*
- * internal part of check_dentry. does the real job.
- *
- */
-
-void check_dent_int (struct dentry *dentry, int parent)
-{
- if (parent) {
- Printk ((KERN_DEBUG "* parent(%d) dentry: %.*s\n", parent, (int) dentry->d_name.len, dentry->d_name.name));
- } else {
- Printk ((KERN_DEBUG "* checking dentry: %.*s\n", (int) dentry->d_name.len, dentry->d_name.name));
- }
- check_inode (dentry->d_inode);
- Printk ((KERN_DEBUG "* d_count=%d", dentry->d_count));
- check_sb (dentry->d_sb, 'd');
- if (dentry->d_op == NULL) {
- Printk ((" (d_op is NULL)\n"));
- } else {
- Printk ((" (d_op is UNKNOWN: %p)\n", dentry->d_op));
- }
-}
-
-/*
- * checks dentry with full traceback to root and prints info. Limited to 10 recursive depths to avoid infinite loops.
- *
- */
-
-void check_dentry_path (struct dentry *dentry, const char *desc)
-{
- int count=0;
- Printk ((KERN_DEBUG "*** check_dentry_path: %.60s\n", desc));
-
- if (!dentry) {
- Printk ((KERN_DEBUG "*** checking dentry... it is NULL !\n"));
- return;
- }
- if (IS_ERR(dentry)) {
- Printk ((KERN_DEBUG "*** checking dentry... it is ERR(%ld) !\n", PTR_ERR(dentry)));
- return;
- }
-
- while (dentry && count < 10) {
- check_dent_int (dentry, count++);
- if (dentry == dentry->d_parent) {
- Printk ((KERN_DEBUG "*** end checking dentry (root reached ok)\n"));
- break;
- }
- dentry = dentry->d_parent;
- }
-
- if (count >= 10) { /* if infinite loop detected */
- Printk ((KERN_ERR "*** WARNING ! INFINITE LOOP ! check_dentry_path aborted !\n"));
- }
-
- if (!dentry) {
- Printk ((KERN_ERR "*** WARNING ! found NULL dentry ! check_dentry_path aborted !\n"));
- }
-}
-#else
-void check_sb (struct super_block *sb, const char c) {};
-void check_inode (struct inode *inode) {};
-void checkd_inode (struct inode *inode) {};
-void check_dentry_path (struct dentry *dentry, const char *desc) {};
-#endif /* UMS_DEBUG */
-
-
-
-/*
- * makes dentry. for name name with length len.
- * if inode is not NULL, puts it also.
- */
-
-struct dentry *creat_dentry (const char *name, const int len, struct inode *inode, struct dentry *parent)
-{
-/* FIXME /mn/: parent is not passed many times... if it is not, dentry should be destroyed before someone else gets to use it */
-
- struct dentry *ret;
- struct qstr qname;
-
- if (inode)
- Printk ((KERN_DEBUG "creat_dentry: creating dentry with inode=%lu for %.*s\n", inode->i_ino, len, name));
- else
- Printk ((KERN_DEBUG "creat_dentry: creating empty dentry for %.*s\n", len, name));
-
- qname.name = name;
- qname.len = len;
-#if 1
- #warning is full_name_hash OK for normal filenames? And for MSDOSFS accessed EMD files?
- qname.hash = full_name_hash (name, len);
-#else
- qname.hash = 0;
-#endif
-
- ret = d_alloc (parent, &qname); /* create new dentry */
- ret->d_inode = NULL;
-
- if (parent) {
-#if 0
- Printk ((KERN_DEBUG "creat_dentry: cloning parent d_op !\n"));
- ret->d_op = parent->d_op;
-#else
- ret->d_op = NULL;
-#endif
- } else {
- ret->d_parent = ret;
- Printk ((KERN_WARNING "creat_dentry: WARNING: NO parent! faking root! beware !\n"));
- }
-
-
- if (inode) {
- if (!ret->d_sb) ret->d_sb = inode->i_sb; /* try to fill it in if available. If available in parent->d_sb, d_alloc will add it automatically */
- d_add (ret, inode);
- }
-
- if (!ret->d_sb) {
- printk (KERN_ERR "creat_dentry: ERROR: NO d_sb !\n");
- } else if (!ret->d_sb->s_dev) {
- printk (KERN_WARNING "creat_dentry: WARNING: NO s_dev. Ugh. !\n");
- }
-
- return ret;
-}
-
-
-/*
- * removes temporary dentry created by creat_dentry
- * it must have d_count of 1, and associated inode i_count of 1
- * to be completely cleared.
- *
- */
-
-void kill_dentry (struct dentry *dentry)
-{
- if (dentry) {
- check_dentry_path (dentry, "KILL_DENTRY B4");
- /* this idea for killing dentry (d_drop/dput pair) from NFS code. dcache.c code&comments seems to agree */
-#if 0
- d_drop (dentry);
- dput (dentry); /* we are done with it */
-#endif
- check_dentry_path (dentry, "KILL_DENTRY AFT");
- } else {
- Printk (("kill_dentry: dentry is NULL ?!\n"));
- }
-
-
- Printk ((KERN_DEBUG "kill_dentry: exiting...\n"));
- return;
-}
-
-
-/*
- * finishes work with dentry
- * it must have d_count of 1, and associated inode i_count of 1
- * to be completely cleared.
- *
- * Currently, this is same as kill_dentry, but this may (will) change.
- * kill_dentry will eventualy be killed (he who lives by the sword, dies
- * by the sword :-) when all faked dentries are nuked out...
- *
- */
-
-void fin_dentry (struct dentry *dentry)
-{
- if (dentry) {
- if (IS_ERR(dentry)) {
- Printk ((KERN_WARNING "fin_dentry: dentry is IS_ERR (%ld)?!\n", PTR_ERR (dentry)));
- } else {
- /* this idea for killing dentry (d_drop/dput pair) from NFS code. dcache.c code&comments seems to agree */
- d_drop (dentry);
- dput (dentry); /* we are done with it */
- }
- } else {
- Printk ((KERN_WARNING "fin_dentry: dentry is NULL ?!\n"));
- }
-
- PRINTK ((KERN_DEBUG "fin_dentry: exiting...\n"));
- return;
-}
-
-
-void UMSDOS_put_inode (struct inode *inode)
-{
- PRINTK ((KERN_DEBUG "put inode %p (%lu) owner %lu pos %lu dir %lu count=%d\n", inode
- ,inode->i_ino
- ,inode->u.umsdos_i.i_emd_owner, inode->u.umsdos_i.pos
- ,inode->u.umsdos_i.i_emd_dir, inode->i_count));
-
- if (inode && pseudo_root && inode == pseudo_root) {
- printk (KERN_ERR "Umsdos: Oops releasing pseudo_root. Notify jacques@solucorp.qc.ca\n");
- }
-
- fat_put_inode (inode);
-}
-
-
-void UMSDOS_put_super (struct super_block *sb)
-{
- Printk ((KERN_DEBUG "UMSDOS_put_super: entering\n"));
- check_dentry_path (sb->s_root, "put_super: START");
- msdos_put_super (sb);
- MOD_DEC_USE_COUNT;
-}
-
-
-
-/*
- * Call msdos_lookup, but set back the original msdos function table.
- * Return 0 if OK, or a negative error code if not.
- */
-int umsdos_real_lookup ( struct inode *dir,
- struct dentry *dentry) /* Will hold inode of the file, if successful */
-{
- int ret;
-
- PRINTK ((KERN_DEBUG "umsdos_real_lookup: looking for %.*s /", dentry->d_name.len, dentry->d_name.name));
- inc_count (dir);
- ret = msdos_lookup (dir, dentry);
- dentry->d_op = NULL; /* FIXME: Not needed? - if it was good once for MSDOS, it will be good any other time also. I hope :) */
- iput (dir); /* pair to inc_count(dir) above */
- PRINTK (("/ returned %d\n", ret));
-
- 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.
- *
- * {i,d}_counts are untouched by this function.
- */
-void umsdos_setup_dir_inode (struct inode *inode)
-{
- inode->u.umsdos_i.i_emd_dir = 0;
- {
- struct inode *emd_dir;
-
- Printk ((KERN_DEBUG "umsdos_setup_dir_inode: Entering for inode=%lu\n", inode->i_ino));
- check_inode (inode);
- emd_dir = umsdos_emd_dir_lookup (inode, 0);
- Printk ((KERN_DEBUG "umsdos_setup_dir_inode: umsdos_emd_dir_lookup for inode=%lu returned %p\n", inode->i_ino, emd_dir));
- check_inode (inode);
- check_inode (emd_dir);
-
- if (emd_dir == NULL) {
- Printk ((KERN_DEBUG "umsdos_setup_dir_inode /mn/: Setting up rdir_inode_ops --> eg. NOT using EMD.\n"));
- inode->i_op = &umsdos_rdir_inode_operations;
- } else {
- Printk ((KERN_DEBUG "umsdos_setup_dir_inode /mn/: Setting up dir_inode_ops --> eg. using EMD.\n"));
- inode->i_op = &umsdos_dir_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;
-
- /* FIXME, I don't have a clue on this one - /mn/ Hmmm? OK? */
-/* Printk ((KERN_WARNING "umsdos_set_dirinfo: /mn/ FIXME: no clue. inode=%lu dir=%lu\n", inode->i_ino, dir->i_ino)); */
- emd_owner = umsdos_emd_dir_lookup (dir, 1);
- Printk (("umsdos_set_dirinfo: emd_owner is %lu for dir %lu\n", emd_owner->i_ino, dir->i_ino));
- inode->u.umsdos_i.i_dir_owner = dir->i_ino;
- inode->u.umsdos_i.i_emd_owner = emd_owner->i_ino;
- /* iput (emd_owner); / * FIXME? */
- inode->u.umsdos_i.pos = f_pos;
-}
-
-
-/*
- * Tells if an Umsdos inode has been "patched" once.
- * Return != 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 atomic_read (&inode->i_count) > 1;
-#endif
-}
-
-
-/*
- * Connect the proper tables in the inode and add some info.
- * i_counts is not changed.
- */
-
-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 too 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 accommodate 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.
- */
-
- Printk ((KERN_DEBUG "Entering umsdos_patch_inode for inode=%lu\n", inode->i_ino));
-
- if (!umsdos_isinit (inode)) {
- inode->u.umsdos_i.i_emd_dir = 0;
- if (S_ISREG (inode->i_mode)) {
- if (MSDOS_SB (inode->i_sb)->cvf_format) {
- if (MSDOS_SB (inode->i_sb)->cvf_format->flags & CVF_USE_READPAGE) {
- Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: setting i_op = umsdos_file_inode_operations_readpage\n"));
- inode->i_op = &umsdos_file_inode_operations_readpage;
- } else {
- Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: setting i_op = umsdos_file_inode_operations_no_bmap\n"));
- inode->i_op = &umsdos_file_inode_operations_no_bmap;
- }
- } else {
- if (inode->i_op->bmap != NULL) {
- Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: setting i_op = umsdos_file_inode_operations\n"));
- inode->i_op = &umsdos_file_inode_operations;
- } else {
- Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: setting i_op = umsdos_file_inode_operations_no_bmap\n"));
- inode->i_op = &umsdos_file_inode_operations_no_bmap;
- }
- }
- } else if (S_ISDIR (inode->i_mode)) {
- if (dir != NULL) {
- umsdos_setup_dir_inode (inode);
- }
- } else if (S_ISLNK (inode->i_mode)) {
- Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: setting i_op = umsdos_symlink_inode_operations\n"));
- inode->i_op = &umsdos_symlink_inode_operations;
- } else if (S_ISCHR (inode->i_mode)) {
- Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: setting i_op = chrdev_inode_operations\n"));
- inode->i_op = &chrdev_inode_operations;
- } else if (S_ISBLK (inode->i_mode)) {
- Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: setting i_op = blkdev_inode_operations\n"));
- inode->i_op = &blkdev_inode_operations;
- } else if (S_ISFIFO (inode->i_mode)) {
- Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: uhm, init_fifo\n"));
- 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()
- */
- Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: here we go: calling umsdos_set_dirinfo (%p,%p,%lu)\n", inode, dir, f_pos));
- 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.
- */
- /* FIXME, again, not a clue */
- struct inode *emd_owner;
-
- Printk ((KERN_WARNING "umsdos_patch_inode: /mn/ Warning: untested emd_owner thingy...\n"));
- emd_owner = umsdos_emd_dir_lookup (dir, 1);
- /* iput (emd_owner); / * FIXME? */
- 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);
- /* iput (dir); / * FIXME: /mn/ added this. Is it OK? */
- ret = 0;
- }
- }
- return ret;
-}
-
-
-
-/*
- * Load an inode from disk.
- */
-void UMSDOS_read_inode (struct inode *inode)
-{
- PRINTK ((KERN_DEBUG "UMSDOS_read_inode %p ino = %lu ", inode, inode->i_ino));
- msdos_read_inode (inode);
- PRINTK (("ino after msdos_read_inode= %lu i_count=%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
- || waitqueue_active (&inode->u.umsdos_i.u.dir_info.p))) {
- 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 completely 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);
-}
-
-
-int internal_notify_change (struct inode *inode, struct iattr *attr)
-{
- int ret = 0;
- struct inode *root;
-
- Printk ((KERN_DEBUG "UMSDOS_notify_change: entering\n"));
-
- if ((ret = inode_change_ok (inode, attr)) != 0)
- return ret;
-
- 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;
-
- root = iget (inode->i_sb, UMSDOS_ROOT_INO);
- if (inode == root) {
- /* #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;
-
- 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;
- struct dentry *emd_dentry;
-
- emd_dentry = geti_dentry (emd_owner); /* FIXME? */
- fill_new_filp (&filp, emd_dentry);
-
- filp.f_pos = inode->u.umsdos_i.pos;
- filp.f_reada = 0;
- Printk (("pos = %Lu ", filp.f_pos));
- /* Read only the start of the entry since we don't touch */
- /* the name */
- ret = umsdos_emd_dir_read (&filp, (char *) &entry, UMSDOS_REC_SIZE);
- if (ret == 0) {
- if (attr->ia_valid & ATTR_UID)
- entry.uid = attr->ia_uid;
- if (attr->ia_valid & ATTR_GID)
- entry.gid = attr->ia_gid;
- if (attr->ia_valid & ATTR_MODE)
- entry.mode = attr->ia_mode;
- if (attr->ia_valid & ATTR_ATIME)
- entry.atime = attr->ia_atime;
- if (attr->ia_valid & ATTR_MTIME)
- entry.mtime = attr->ia_mtime;
- if (attr->ia_valid & ATTR_CTIME)
- entry.ctime = attr->ia_ctime;
-
- entry.nlink = inode->i_nlink;
- filp.f_pos = inode->u.umsdos_i.pos;
- ret = umsdos_emd_dir_write (&filp, (char *) &entry, UMSDOS_REC_SIZE);
-
- Printk (("notify pos %lu 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.
- */
- }
- /* iput (emd_owner); / * FIXME? /mn/ */
- }
- Printk (("\n"));
- }
- /* iput (root); / * FIXME - /mn/ This is should be OK. */
- }
- if (ret == 0)
- inode_setattr (inode, attr);
-
- return ret;
-}
-
-
-int UMSDOS_notify_change (struct dentry *dentry, struct iattr *attr)
-{
- return internal_notify_change (dentry->d_inode, attr);
-}
-
-/*
- * Update the disk with the inode content
- */
-void UMSDOS_write_inode (struct inode *inode)
-{
- struct iattr newattrs;
-
- PRINTK (("UMSDOS_write_inode emd %d (FIXME: missing notify_change)\n", inode->u.umsdos_i.i_emd_owner));
- fat_write_inode (inode);
- newattrs.ia_mtime = inode->i_mtime;
- newattrs.ia_atime = inode->i_atime;
- newattrs.ia_ctime = inode->i_ctime;
- newattrs.ia_valid = ATTR_MTIME | ATTR_ATIME | ATTR_CTIME;
- /*
- * UMSDOS_notify_change is convenient to call here
- * to update the EMD entry associated with this inode.
- * But it has the side effect to re"dirt" the inode.
- */
-
-/*
- * internal_notify_change (inode, &newattrs);
- * inode->i_state &= ~I_DIRTY; / * FIXME: this doesn't work. We need to remove ourselves from list on dirty inodes. /mn/ */
-}
-
-
-
-
-
-/* #Specification: function name / convention
- * A simple convention for function names has been used in
- * the UMSDOS filesystem. First, all functions use the prefix
- * umsdos_ to avoid name clashes with other parts of the kernel.
- *
- * Standard VFS entry points use the prefix UMSDOS (upper case)
- * so it's easier to tell them apart.
- * N.B. (FIXME) PTW, the order and contents of this struct changed.
- */
-
-static struct super_operations umsdos_sops =
-{
- UMSDOS_read_inode, /* read_inode */
- UMSDOS_write_inode, /* write_inode */
- UMSDOS_put_inode, /* put_inode */
- fat_delete_inode, /* delete_inode */
- UMSDOS_notify_change, /* notify_change */
- UMSDOS_put_super, /* put_super */
- NULL, /* write_super */
- fat_statfs, /* statfs */
- NULL /* remount_fs */
-};
-
-/*
- * Read the super block of an Extended MS-DOS FS.
- */
-struct super_block *UMSDOS_read_super ( struct super_block *sb,
- 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 sense 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 *res;
- struct inode *pseudo = NULL;
-
- Printk ((KERN_DEBUG "UMSDOS /mn/: starting UMSDOS_read_super\n"));
- MOD_INC_USE_COUNT;
- Printk ((KERN_DEBUG "UMSDOS /mn/: sb = %p\n", sb));
-
- MSDOS_SB(sb)->options.isvfat = 0;
- res = msdos_read_super (sb, data, silent);
- sb->s_op = &umsdos_sops;
- Printk ((KERN_DEBUG "UMSDOS /mn/: res = %p\n", res));
- printk (KERN_INFO "UMSDOS dentry-WIP-Beta 0.82-7 (compatibility level %d.%d, fast msdos)\n", UMSDOS_VERSION, UMSDOS_RELEASE);
-
- if (res == NULL) {
- sb->s_dev = 0;
- MOD_DEC_USE_COUNT;
- Printk ((KERN_DEBUG "UMSDOS: msdos_read_super failed ! mount aborted.\n"));
- return NULL;
- }
-
- MSDOS_SB (res)->options.dotsOK = 0; /* disable hidden==dotfile */
-#if 1
- res->s_root->d_op = NULL; /* FIXME:?? clear d_op on root so it will not be inherited */
-#endif
-
- Printk ((KERN_DEBUG "umsdos /mn/: here goes the iget ROOT_INO\n"));
-
-/* pseudo = iget (res, UMSDOS_ROOT_INO); // we probably could do it as below (and remove iput() below), but we want use_count to go up. Do we ? :) */
- pseudo = res->s_root->d_inode; /* msdos_read_super already did iget() it */
-
- Printk ((KERN_DEBUG "umsdos_read_super pseudo=%p\n", pseudo));
-
- umsdos_setup_dir_inode (pseudo);
- Printk ((KERN_DEBUG "umsdos_setup_dir_inode passed. pseudo i_count=%d\n", pseudo->i_count));
-
- /* if (s == super_blocks){ FIXME, super_blocks no longer exported */
- if (pseudo) {
-#if 0 /* FIXME URGENT: disable pseudo root-for the moment of testing. re-enable this before release ! */
- /* #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 or
- * /linux/sbin/init. 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 dentry *root, *etc, *etc_rc, *sbin, *init = NULL;
-
- root = creat_dentry (UMSDOS_PSDROOT_NAME, strlen (UMSDOS_PSDROOT_NAME), NULL, res->s_root);
- sbin = creat_dentry ("sbin", 4, NULL, NULL); /* FIXME: should last NULL be root or res->s_root ? Not NULL in any case.. */
-
- Printk ((KERN_DEBUG "Mounting root\n"));
- if (umsdos_real_lookup (pseudo, root) == 0
- && (root->d_inode != NULL)
- && S_ISDIR (root->d_inode->i_mode)) {
-
- int pseudo_ok = 0;
-
- Printk ((KERN_DEBUG "/%s is there\n", UMSDOS_PSDROOT_NAME));
- etc = creat_dentry ("etc", 3, NULL, root);
-
-
- if (umsdos_real_lookup (pseudo, etc) == 0
- && S_ISDIR (etc->d_inode->i_mode)) {
-
- Printk ((KERN_DEBUG "/%s/etc is there\n", UMSDOS_PSDROOT_NAME));
-
- init = creat_dentry ("init", 4, NULL, etc);
- etc_rc = creat_dentry ("rc", 2, NULL, etc);
-
- if ((umsdos_real_lookup (pseudo, init) == 0
- && S_ISREG (init->d_inode->i_mode))
- || (umsdos_real_lookup (pseudo, etc_rc) == 0
- && S_ISREG (etc_rc->d_inode->i_mode))) {
- pseudo_ok = 1;
- }
- /* iput (pseudo); iput (pseudo); / * because msdos_real_lookup does inc_count (pseudo) */
-
- /* FIXME !!!!!! */
- /* iput(init); */
- /* iput(rc); */
- }
- if (!pseudo_ok
- /* && umsdos_real_lookup (pseudo, "sbin", 4, sbin)==0 */
- && umsdos_real_lookup (pseudo, sbin) == 0
- && S_ISDIR (sbin->d_inode->i_mode)) {
-
- Printk ((KERN_DEBUG "/%s/sbin is there\n", UMSDOS_PSDROOT_NAME));
- if (umsdos_real_lookup (pseudo, init) == 0
- && S_ISREG (init->d_inode->i_mode)) {
- pseudo_ok = 1;
- }
- /*iput (pseudo);*/
- /* FIXME !!!
- * iput (init); */
- }
- if (pseudo_ok) {
- umsdos_setup_dir_inode (pseudo);
- Printk ((KERN_INFO "Activating pseudo root /%s\n", UMSDOS_PSDROOT_NAME));
- pseudo_root = pseudo;
- inc_count (pseudo);
- pseudo = NULL;
- }
- /* FIXME
- *
- * iput (sbin);
- * iput (etc);
- */
- }
-#endif
- /*iput (pseudo); // iget was removed... so this no longer needed ? */
- }
-#if 1
- #warning UMSDOS: using ugly mount kludge only if necessary (DEBUG)
- if (res->s_root->d_count != 1) { /* if it is not 1, mount will fail with -EBUSY! */
- printk (KERN_ERR "UMSDOS: mount kludge activated: root d_count was %d !\n", res->s_root->d_count);
- res->s_root->d_count = 1;
- }
-#endif
- check_dentry_path (res->s_root, "ROOT dentry check");
- Printk ((KERN_DEBUG "umsdos_read_super /mn/: (pseudo=%lu, i_count=%d) returning %p\n", pseudo->i_ino, pseudo->i_count, res));
- return res;
-}
-
-
-
-static struct file_system_type umsdos_fs_type =
-{
- "umsdos",
- FS_REQUIRES_DEV,
- UMSDOS_read_super,
- NULL
-};
-
-__initfunc (int init_umsdos_fs (void))
-{
- return register_filesystem (&umsdos_fs_type);
-}
-
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-
-int init_module (void)
-{
- return init_umsdos_fs ();
-}
-
-void cleanup_module (void)
-{
- unregister_filesystem (&umsdos_fs_type);
-}
-
-#endif
+/*
+ * linux/fs/umsdos/inode.c
+ *
+ * Written 1993 by Jacques Gelinas
+ * Inspired from linux/fs/msdos/... by Werner Almesberger
+ */
+
+#include <linux/module.h>
+
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <asm/uaccess.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/umsdos_fs.h>
+#include <linux/list.h>
+
+extern struct inode_operations umsdos_rdir_inode_operations;
+
+
+struct inode *pseudo_root = NULL; /* Useful to simulate the pseudo DOS */
+ /* directory. See UMSDOS_readdir_x() */
+
+
+/*
+ * returns inode->i_dentry
+ * Note: Deprecated; won't work reliably
+ */
+
+struct dentry *geti_dentry (struct inode *inode)
+{
+ struct dentry *ret;
+
+ if (!inode) {
+ printk (KERN_ERR "geti_dentry: ERROR: inode is NULL!\n");
+ return NULL;
+ }
+ if (list_empty(&inode->i_dentry)) {
+ printk (KERN_WARNING
+ "geti_dentry: WARNING: no dentry for inode %ld\n",
+ inode->i_ino);
+ return NULL;
+ }
+ ret = list_entry (inode->i_dentry.next, struct dentry, d_alias);
+
+ PRINTK ((KERN_DEBUG "geti_dentry : inode %lu, dentry is %s/%s\n",
+ inode->i_ino, ret->d_parent->d_name.name, ret->d_name.name));
+ return ret;
+}
+
+
+/*
+ * Initialize a private filp
+ */
+void fill_new_filp (struct file *filp, struct dentry *dentry)
+{
+ if (!dentry)
+ printk("fill_new_filp: NULL dentry!\n");
+
+ memset (filp, 0, sizeof (struct file));
+ filp->f_reada = 1;
+ filp->f_flags = O_RDWR;
+ filp->f_dentry = dentry;
+ filp->f_op = &umsdos_file_operations;
+}
+
+
+/*
+ * makes dentry. for name name with length len.
+ * if inode is not NULL, puts it also.
+ * Note: Deprecated; use umsdos_lookup_dentry
+ */
+
+struct dentry *creat_dentry (const char *name, const int len,
+ struct inode *inode, struct dentry *parent)
+{
+/* FIXME /mn/: parent is not passed many times... if it is not, dentry should be destroyed before someone else gets to use it */
+
+ struct dentry *ret;
+ struct qstr qname;
+
+ if (inode)
+ Printk ((KERN_DEBUG "creat_dentry: creating dentry with inode=%lu for %.*s\n", inode->i_ino, len, name));
+ else
+ Printk ((KERN_DEBUG "creat_dentry: creating empty dentry for %.*s\n", len, name));
+
+ qname.name = name;
+ qname.len = len;
+ qname.hash = full_name_hash (name, len);
+
+ ret = d_alloc (parent, &qname); /* create new dentry */
+
+ if (parent) {
+#if 0
+ Printk ((KERN_DEBUG "creat_dentry: cloning parent d_op !\n"));
+ ret->d_op = parent->d_op;
+#else
+ ret->d_op = NULL;
+#endif
+ } else {
+ ret->d_parent = ret;
+ Printk ((KERN_WARNING "creat_dentry: WARNING: NO parent! faking root! beware !\n"));
+ }
+
+
+ if (inode) {
+ /* try to fill it in if available. If available in
+ * parent->d_sb, d_alloc will add it automatically
+ */
+ if (!ret->d_sb) ret->d_sb = inode->i_sb;
+ d_add (ret, inode);
+ }
+
+ if (!ret->d_sb) {
+ printk (KERN_ERR "creat_dentry: ERROR: NO d_sb !\n");
+ } else if (!ret->d_sb->s_dev) {
+ printk (KERN_WARNING "creat_dentry: WARNING: NO s_dev. Ugh. !\n");
+ }
+
+ return ret;
+}
+
+
+void UMSDOS_put_inode (struct inode *inode)
+{
+ PRINTK ((KERN_DEBUG
+ "put inode %p (%lu) owner %lu pos %lu dir %lu count=%d\n"
+ ,inode, inode->i_ino
+ ,inode->u.umsdos_i.i_emd_owner, inode->u.umsdos_i.pos
+ ,inode->u.umsdos_i.i_emd_dir, inode->i_count));
+
+ if (inode && pseudo_root && inode == pseudo_root) {
+ printk (KERN_ERR "Umsdos: Oops releasing pseudo_root."
+ " Notify jacques@solucorp.qc.ca\n");
+ }
+
+ fat_put_inode (inode);
+}
+
+
+void UMSDOS_put_super (struct super_block *sb)
+{
+ Printk ((KERN_DEBUG "UMSDOS_put_super: entering\n"));
+ msdos_put_super (sb);
+ MOD_DEC_USE_COUNT;
+}
+
+
+/*
+ * Call msdos_lookup, but set back the original msdos function table.
+ * Return 0 if OK, or a negative error code if not.
+ * Dentry will hold inode of the file, if successful
+ */
+int umsdos_real_lookup (struct inode *dir, struct dentry *dentry)
+{
+ int ret;
+
+ PRINTK ((KERN_DEBUG "umsdos_real_lookup: looking for %s/%s /",
+ dentry->d_parent->d_name.name, dentry->d_name.name));
+ ret = msdos_lookup (dir, dentry);
+ PRINTK (("/ returned %d\n", ret));
+
+ return ret;
+}
+
+/*
+ * Complete the setup of an directory dentry.
+ * 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.
+ *
+ * {i,d}_counts are untouched by this function.
+ */
+void umsdos_setup_dir(struct dentry *dir)
+{
+ struct inode *inode = dir->d_inode;
+
+ if (!S_ISDIR(inode->i_mode))
+ printk(KERN_ERR "umsdos_setup_dir: %s/%s not a dir!\n",
+ dir->d_parent->d_name.name, dir->d_name.name);
+
+ inode->u.umsdos_i.i_emd_dir = 0;
+ inode->i_op = &umsdos_rdir_inode_operations;
+ if (umsdos_have_emd(dir)) {
+Printk((KERN_DEBUG "umsdos_setup_dir: %s/%s using EMD\n",
+dir->d_parent->d_name.name, dir->d_name.name));
+ inode->i_op = &umsdos_dir_inode_operations;
+ }
+}
+
+/*
+ * 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.
+ *
+ * {i,d}_counts are untouched by this function.
+ * Note: Deprecated; use above function if possible.
+ */
+void umsdos_setup_dir_inode (struct inode *inode)
+{
+ struct inode *emd_dir;
+
+ inode->u.umsdos_i.i_emd_dir = 0;
+
+ Printk ((KERN_DEBUG
+ "umsdos_setup_dir_inode: Entering for inode=%lu\n",
+ inode->i_ino));
+ check_inode (inode);
+ emd_dir = umsdos_emd_dir_lookup (inode, 0);
+ Printk ((KERN_DEBUG "umsdos_setup_dir_inode: "
+ "umsdos_emd_dir_lookup for inode=%lu returned %p\n",
+ inode->i_ino, emd_dir));
+ check_inode (inode);
+ check_inode (emd_dir);
+
+ inode->i_op = &umsdos_rdir_inode_operations;
+ if (emd_dir) {
+ Printk ((KERN_DEBUG "umsdos_setup_dir_inode: using EMD.\n"));
+ inode->i_op = &umsdos_dir_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);
+
+ if (!emd_owner)
+ goto out;
+ Printk (("umsdos_set_dirinfo: emd_owner is %lu for dir %lu\n",
+ emd_owner->i_ino, dir->i_ino));
+ inode->u.umsdos_i.i_dir_owner = dir->i_ino;
+ inode->u.umsdos_i.i_emd_owner = emd_owner->i_ino;
+ inode->u.umsdos_i.pos = f_pos;
+ iput (emd_owner);
+out:
+ return;
+}
+
+
+/*
+ * Tells if an Umsdos inode has been "patched" once.
+ * Return != 0 if so.
+ */
+int umsdos_isinit (struct inode *inode)
+{
+ return inode->u.umsdos_i.i_emd_owner != 0;
+}
+
+
+/*
+ * Connect the proper tables in the inode and add some info.
+ * i_counts is not changed.
+ *
+ * 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 too 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 accommodate 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.
+ */
+/* #Specification: inode / umsdos info
+ * The first time an inode is seen (inode->i_count == 1),
+ * the inode number of the EMD file which controls this inode
+ * is tagged to this inode. It allows operations such as
+ * notify_change to be handled.
+ */
+void umsdos_patch_inode (struct inode *inode, struct inode *dir, off_t f_pos)
+{
+ Printk ((KERN_DEBUG "Entering umsdos_patch_inode for inode=%lu\n",
+ inode->i_ino));
+
+ if (umsdos_isinit (inode))
+ goto already_init;
+
+ inode->u.umsdos_i.i_emd_dir = 0;
+ if (S_ISREG (inode->i_mode)) {
+ if (MSDOS_SB (inode->i_sb)->cvf_format) {
+ if (MSDOS_SB (inode->i_sb)->cvf_format->flags & CVF_USE_READPAGE) {
+Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: setting i_op = umsdos_file_inode_operations_readpage\n"));
+ inode->i_op = &umsdos_file_inode_operations_readpage;
+ } else {
+Printk ((KERN_DEBUG "umsdos_patch_inode: umsdos_file_inode_ops_no_bmap\n"));
+ inode->i_op = &umsdos_file_inode_operations_no_bmap;
+ }
+ } else {
+ if (inode->i_op->bmap != NULL) {
+Printk ((KERN_DEBUG "umsdos_patch_inode: umsdos_file_inode_ops\n"));
+ inode->i_op = &umsdos_file_inode_operations;
+ } else {
+Printk ((KERN_DEBUG "umsdos_patch_inode: umsdos_file_inode_ops_no_bmap\n"));
+ inode->i_op = &umsdos_file_inode_operations_no_bmap;
+ }
+ }
+ } else if (S_ISDIR (inode->i_mode)) {
+ if (dir != NULL) {
+ umsdos_setup_dir_inode (inode);
+ }
+ } else if (S_ISLNK (inode->i_mode)) {
+ Printk ((KERN_DEBUG
+ "umsdos_patch_inode: umsdos_symlink_inode_ops\n"));
+ inode->i_op = &umsdos_symlink_inode_operations;
+ } else if (S_ISCHR (inode->i_mode)) {
+ Printk ((KERN_DEBUG "umsdos_patch_inode: chrdev_inode_ops\n"));
+ inode->i_op = &chrdev_inode_operations;
+ } else if (S_ISBLK (inode->i_mode)) {
+ Printk ((KERN_DEBUG "umsdos_patch_inode: blkdev_inode_ops\n"));
+ inode->i_op = &blkdev_inode_operations;
+ } else if (S_ISFIFO (inode->i_mode)) {
+ Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: uhm, init_fifo\n"));
+ init_fifo (inode);
+ }
+ if (dir != NULL) {
+ /*
+ * This is done last because it also control the
+ * status of umsdos_isinit()
+ */
+ Printk ((KERN_DEBUG
+ "umsdos_patch_inode: call x_set_dirinfo(%p,%p,%lu)\n",
+ inode, dir, f_pos));
+ umsdos_set_dirinfo (inode, dir, f_pos);
+ }
+ return;
+
+already_init:
+ 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);
+ if (!emd_owner)
+ goto out;
+ 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);
+ }
+ iput (emd_owner);
+ out:
+ return;
+ }
+}
+
+/*
+ * Patch the inode in a dentry.
+ */
+void umsdos_patch_dentry_inode(struct dentry *dentry, off_t f_pos)
+{
+ umsdos_patch_inode(dentry->d_inode, dentry->d_parent->d_inode, f_pos);
+}
+
+
+/*
+ * Load an inode from disk.
+ */
+/* #Specification: Inode / post initialisation
+ * To completely 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.
+ */
+void UMSDOS_read_inode (struct inode *inode)
+{
+ PRINTK ((KERN_DEBUG "UMSDOS_read_inode %p ino = %lu ",
+ inode, inode->i_ino));
+ msdos_read_inode (inode);
+ PRINTK (("ino after msdos_read_inode= %lu i_count=%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
+ || waitqueue_active (&inode->u.umsdos_i.u.dir_info.p))) {
+ 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));
+ }
+
+ /* N.B. Defer this until we have a dentry ... */
+ umsdos_patch_inode (inode, NULL, 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.
+ */
+static int internal_notify_change (struct inode *inode, struct iattr *attr)
+{
+ unsigned long i_emd_owner = inode->u.umsdos_i.i_emd_owner;
+ int ret;
+
+ Printk ((KERN_DEBUG "UMSDOS_notify_change: entering\n"));
+
+ if ((ret = inode_change_ok (inode, attr)) != 0)
+ goto out;
+
+ if (inode->i_nlink == 0)
+ goto out_nolink;
+
+ if (inode->i_ino == UMSDOS_ROOT_INO)
+ goto out_nolink;
+
+ 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;
+ struct file filp;
+ struct umsdos_dirent entry;
+ struct dentry *emd_dentry;
+
+ Printk (("notify change %p ", inode));
+ ret = -EPERM;
+ emd_owner = iget (inode->i_sb, i_emd_owner);
+ if (!emd_owner) {
+ printk ("UMSDOS: emd_owner = NULL ???");
+ goto out_nolink;
+ }
+ emd_dentry = geti_dentry (emd_owner); /* FIXME? */
+ fill_new_filp (&filp, emd_dentry);
+
+ filp.f_pos = inode->u.umsdos_i.pos;
+ filp.f_reada = 0;
+ Printk (("pos = %Lu ", filp.f_pos));
+ /* Read only the start of the entry since we don't touch */
+ /* the name */
+ ret = umsdos_emd_dir_read (&filp, (char *) &entry,
+ UMSDOS_REC_SIZE);
+ if (!ret) {
+ if (attr->ia_valid & ATTR_UID)
+ entry.uid = attr->ia_uid;
+ if (attr->ia_valid & ATTR_GID)
+ entry.gid = attr->ia_gid;
+ if (attr->ia_valid & ATTR_MODE)
+ entry.mode = attr->ia_mode;
+ if (attr->ia_valid & ATTR_ATIME)
+ entry.atime = attr->ia_atime;
+ if (attr->ia_valid & ATTR_MTIME)
+ entry.mtime = attr->ia_mtime;
+ if (attr->ia_valid & ATTR_CTIME)
+ entry.ctime = attr->ia_ctime;
+
+ entry.nlink = inode->i_nlink;
+ filp.f_pos = inode->u.umsdos_i.pos;
+ ret = umsdos_emd_dir_write (&filp, (char *) &entry,
+ UMSDOS_REC_SIZE);
+
+ Printk (("notify pos %lu 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.
+ */
+ }
+ iput(emd_owner);
+ }
+out_nolink:
+ if (ret == 0)
+ inode_setattr (inode, attr);
+out:
+ return ret;
+}
+
+
+int UMSDOS_notify_change (struct dentry *dentry, struct iattr *attr)
+{
+ return internal_notify_change (dentry->d_inode, attr);
+}
+
+/*
+ * Update the disk with the inode content
+ */
+void UMSDOS_write_inode (struct inode *inode)
+{
+ struct iattr newattrs;
+
+ PRINTK (("UMSDOS_write_inode emd %d (FIXME: missing notify_change)\n",
+ inode->u.umsdos_i.i_emd_owner));
+ fat_write_inode (inode);
+ newattrs.ia_mtime = inode->i_mtime;
+ newattrs.ia_atime = inode->i_atime;
+ newattrs.ia_ctime = inode->i_ctime;
+ newattrs.ia_valid = ATTR_MTIME | ATTR_ATIME | ATTR_CTIME;
+ /*
+ * UMSDOS_notify_change is convenient to call here
+ * to update the EMD entry associated with this inode.
+ * But it has the side effect to re"dirt" the inode.
+ */
+/*
+ * internal_notify_change (inode, &newattrs);
+ * inode->i_state &= ~I_DIRTY; / * FIXME: this doesn't work. We need to remove ourselves from list on dirty inodes. /mn/ */
+}
+
+
+static struct super_operations umsdos_sops =
+{
+ UMSDOS_read_inode, /* read_inode */
+ UMSDOS_write_inode, /* write_inode */
+ UMSDOS_put_inode, /* put_inode */
+ fat_delete_inode, /* delete_inode */
+ UMSDOS_notify_change, /* notify_change */
+ UMSDOS_put_super, /* put_super */
+ NULL, /* write_super */
+ fat_statfs, /* statfs */
+ NULL /* remount_fs */
+};
+
+/*
+ * Read the super block of an Extended MS-DOS FS.
+ */
+struct super_block *UMSDOS_read_super (struct super_block *sb, void *data,
+ int silent)
+{
+ struct super_block *res;
+ struct inode *pseudo = NULL;
+
+ MOD_INC_USE_COUNT;
+ MSDOS_SB(sb)->options.isvfat = 0;
+ /*
+ * Call msdos-fs to mount the disk.
+ * Note: this returns res == sb or NULL
+ */
+ res = msdos_read_super (sb, data, silent);
+ if (!res)
+ goto out_fail;
+
+ printk (KERN_INFO "UMSDOS dentry-WIP-Beta 0.82-7 "
+ "(compatibility level %d.%d, fast msdos)\n",
+ UMSDOS_VERSION, UMSDOS_RELEASE);
+
+ sb->s_op = &umsdos_sops;
+ MSDOS_SB(sb)->options.dotsOK = 0; /* disable hidden==dotfile */
+
+ /* FIXME:?? clear d_op on root so it will not be inherited */
+ sb->s_root->d_op = NULL;
+
+ pseudo = sb->s_root->d_inode;
+ umsdos_setup_dir(sb->s_root);
+
+#if 0
+ if (pseudo) {
+ pseudo_root_stuff();
+ }
+#endif
+
+ /* if d_count is not 1, mount will fail with -EBUSY! */
+ if (sb->s_root->d_count > 1) {
+ shrink_dcache_sb(sb);
+ }
+ return sb;
+
+out_fail:
+ printk(KERN_INFO "UMSDOS: msdos_read_super failed, mount aborted.\n");
+ sb->s_dev = 0;
+ MOD_DEC_USE_COUNT;
+ return NULL;
+}
+
+/*
+ * FIXME URGENT:
+ * disable pseudo root-for the moment of testing.
+ * re-enable this before release !
+ */
+#if 0
+void pseudo_root_stuff(void)
+{
+ struct dentry *root, *etc, *etc_rc, *sbin, *init = NULL;
+
+ root = creat_dentry (UMSDOS_PSDROOT_NAME,
+ strlen (UMSDOS_PSDROOT_NAME),
+ NULL, res->s_root);
+ sbin = creat_dentry ("sbin", 4, NULL, root);
+
+ Printk ((KERN_DEBUG "Mounting root\n"));
+ if (umsdos_real_lookup (pseudo, root) == 0
+ && (root->d_inode != NULL)
+ && S_ISDIR (root->d_inode->i_mode)) {
+
+ int pseudo_ok = 0;
+
+Printk ((KERN_DEBUG "/%s is there\n", UMSDOS_PSDROOT_NAME));
+ etc = creat_dentry ("etc", 3, NULL, root);
+
+
+ if (umsdos_real_lookup (pseudo, etc) == 0
+ && S_ISDIR (etc->d_inode->i_mode)) {
+
+Printk ((KERN_DEBUG "/%s/etc is there\n", UMSDOS_PSDROOT_NAME));
+
+ init = creat_dentry ("init", 4, NULL, etc);
+ etc_rc = creat_dentry ("rc", 2, NULL, etc);
+
+ if ((umsdos_real_lookup (pseudo, init) == 0
+ && S_ISREG (init->d_inode->i_mode))
+ || (umsdos_real_lookup (pseudo, etc_rc) == 0
+ && S_ISREG (etc_rc->d_inode->i_mode))) {
+ pseudo_ok = 1;
+ }
+
+ /* FIXME !!!!!! */
+ /* iput(init); */
+ /* iput(rc); */
+ }
+ if (!pseudo_ok
+ /* && umsdos_real_lookup (pseudo, "sbin", 4, sbin)==0 */
+ && umsdos_real_lookup (pseudo, sbin) == 0
+ && S_ISDIR (sbin->d_inode->i_mode)) {
+
+Printk ((KERN_DEBUG "/%s/sbin is there\n", UMSDOS_PSDROOT_NAME));
+ if (umsdos_real_lookup (pseudo, init) == 0
+ && S_ISREG (init->d_inode->i_mode)) {
+ pseudo_ok = 1;
+ }
+ /* FIXME !!!
+ * iput (init); */
+ }
+ if (pseudo_ok) {
+ umsdos_setup_dir_inode (pseudo);
+Printk ((KERN_INFO "Activating pseudo root /%s\n", UMSDOS_PSDROOT_NAME));
+ pseudo_root = pseudo;
+ inc_count (pseudo);
+ pseudo = NULL;
+ }
+ /* FIXME
+ *
+ * iput (sbin);
+ * iput (etc);
+ */
+ }
+}
+#endif
+
+
+static struct file_system_type umsdos_fs_type =
+{
+ "umsdos",
+ FS_REQUIRES_DEV,
+ UMSDOS_read_super,
+ NULL
+};
+
+__initfunc (int init_umsdos_fs (void))
+{
+ return register_filesystem (&umsdos_fs_type);
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+int init_module (void)
+{
+ return init_umsdos_fs ();
+}
+
+void cleanup_module (void)
+{
+ unregister_filesystem (&umsdos_fs_type);
+}
+
+#endif
-/*
- * linux/fs/umsdos/ioctl.c
- *
- * Written 1993 by Jacques Gelinas
- *
- * Extended MS-DOS ioctl directory handling functions
- */
-
-#include <asm/uaccess.h>
-#include <linux/errno.h>
-#include <linux/mm.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/fs.h>
-#include <linux/msdos_fs.h>
-#include <linux/umsdos_fs.h>
-
-struct UMSDOS_DIR_ONCE {
- struct dirent *ent;
- int count;
-};
-
-/*
- * Record a single entry the first call.
- * Return -EINVAL the next one.
- */
-static int umsdos_ioctl_fill (
- void *buf,
- const char *name,
- int name_len,
- off_t offset,
- ino_t ino)
-{
- int ret = -EINVAL;
- struct UMSDOS_DIR_ONCE *d = (struct UMSDOS_DIR_ONCE *) buf;
-
- if (d->count == 0) {
- copy_to_user (d->ent->d_name, name, name_len);
- put_user ('\0', d->ent->d_name + name_len);
- put_user (name_len, &d->ent->d_reclen);
- put_user (ino, &d->ent->d_ino);
- put_user (offset, &d->ent->d_off);
- d->count = 1;
- ret = 0;
- }
- return ret;
-}
-
-
-/*
- * 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;
- int err;
- struct dentry *old_dent;
-
- /* forward non-umsdos ioctls - this hopefully doesn't cause conflicts */
- if (cmd != UMSDOS_GETVERSION
- && cmd != UMSDOS_READDIR_DOS
- && cmd != UMSDOS_READDIR_EMD
- && cmd != UMSDOS_INIT_EMD
- && cmd != UMSDOS_CREAT_EMD
- && cmd != UMSDOS_RENAME_DOS
- && cmd != UMSDOS_UNLINK_EMD
- && cmd != UMSDOS_UNLINK_DOS
- && cmd != UMSDOS_RMDIR_DOS
- && cmd != UMSDOS_STAT_DOS
- && cmd != UMSDOS_DOS_SETUP)
- return fat_dir_ioctl (dir, filp, cmd, data);
-
- /* #Specification: ioctl / acces
- * Only root (effective id) is allowed to do IOCTL on directory
- * in UMSDOS. EPERM is returned for other user.
- */
- /*
- * Well, not all cases require write access, but it simplifies
- * the code, and let's face it, there is only one client (umssync)
- * for all this.
- */
- if ((err = verify_area (VERIFY_WRITE, (void *) data, sizeof (struct umsdos_ioctl))) < 0) {
- ret = err;
- } else 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 independently. These are essential to implement
- * the synchronise.
- */
- 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_user (UMSDOS_VERSION, &idata->version);
- put_user (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.
- */
- struct UMSDOS_DIR_ONCE bufk;
-
- bufk.count = 0;
- bufk.ent = &idata->dos_dirent;
-
- fat_readdir (filp, &bufk, umsdos_ioctl_fill);
-
- ret = bufk.count == 1 ? 1 : 0;
- } 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.
- */
-
- old_dent = filp->f_dentry;
- if (fix_emd_filp (filp) == 0) {
- while (1) {
- if (filp->f_pos >= filp->f_dentry->d_inode->i_size) {
- ret = 0;
- break;
- } else {
- struct umsdos_dirent entry;
- off_t f_pos = filp->f_pos;
-
- ret = umsdos_emd_dir_readentry (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);
- copy_to_user (&idata->umsdos_dirent, &entry, sizeof (entry));
- copy_to_user (&idata->dos_dirent.d_name, info.fake.fname, info.fake.len + 1);
- break;
- }
- }
- }
- fin_dentry (filp->f_dentry);
- filp->f_dentry = old_dent;
- /* iput (filp->f_dentry->d_inode); / * FIXME? */
- } 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); / * FIXME?? */
-
- dir->i_op = ret
- ? &umsdos_dir_inode_operations
- : &umsdos_rdir_inode_operations;
- } else {
- struct umsdos_ioctl data;
-
- copy_from_user (&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_RENAME_DOS) {
- struct dentry *old_dentry, *new_dentry; /* FIXME */
-
- /* #Specification: ioctl / UMSDOS_RENAME_DOS
- * A file or directory is rename in a DOS directory
- * (not moved across directory). The source name
- * is in the dos_dirent.name field and the destination
- * is in umsdos_dirent.name field.
- *
- * This ioctl allows umssync to rename a mangle file
- * name before syncing it back in the EMD.
- */
- inc_count (dir);
- inc_count (dir);
- /*
- * ret = msdos_rename (dir
- * ,data.dos_dirent.d_name,data.dos_dirent.d_reclen
- * ,dir
- * ,data.umsdos_dirent.name,data.umsdos_dirent.name_len);
- */
- old_dentry = creat_dentry (data.dos_dirent.d_name, data.dos_dirent.d_reclen, NULL, geti_dentry (dir)); /* FIXME: probably should fill inode part */
- new_dentry = creat_dentry (data.umsdos_dirent.name, data.umsdos_dirent.name_len, NULL, geti_dentry (dir));
- ret = msdos_rename (dir, old_dentry, dir, new_dentry);
- } 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) {
- struct dentry *dentry, *dp;
-
- /* #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.
- */
- dp = geti_dentry (dir);
- dentry = compat_umsdos_real_lookup (dp, data.dos_dirent.d_name, data.dos_dirent.d_reclen);
- ret = msdos_unlink (dir, dentry);
- dput (dentry); /* FIXME: is this OK now? */
-
- } else if (cmd == UMSDOS_RMDIR_DOS) {
- struct dentry *dentry, *dp;
-
- /* #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.
- */
- dp = geti_dentry (dir);
- dentry = compat_umsdos_real_lookup (dp, data.dos_dirent.d_name, data.dos_dirent.d_reclen);
- ret = msdos_rmdir (dir, dentry);
- dput (dentry); /* FIXME: is this OK now? */
-
- } 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;
- struct dentry *d_dir, *dret;
-
- d_dir = geti_dentry (dir);
- dret = compat_umsdos_real_lookup (d_dir, data.dos_dirent.d_name, data.dos_dirent.d_reclen);
- if (dret) {
- inode = dret->d_inode;
- 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;
- copy_to_user (&idata->stat, &data.stat, sizeof (data.stat));
- fin_dentry (dret);
- }
- } else if (cmd == UMSDOS_DOS_SETUP) {
- /* #Specification: ioctl / UMSDOS_DOS_SETUP
- * The UMSDOS_DOS_SETUP ioctl allow changing the
- * default permission of the MS-DOS filesystem driver
- * on the fly. The MS-DOS driver applies global permissions
- * 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 applies ONLY to MS-DOS directories
- * (those without EMD --linux-.---). Umsdos directory
- * have independent (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.options.fs_uid = data.umsdos_dirent.uid;
- dir->i_sb->u.msdos_sb.options.fs_gid = data.umsdos_dirent.gid;
- dir->i_sb->u.msdos_sb.options.fs_umask = data.umsdos_dirent.mode;
- ret = 0;
- }
- }
- }
- Printk (("ioctl return %d\n", ret));
- return ret;
-}
+/*
+ * linux/fs/umsdos/ioctl.c
+ *
+ * Written 1993 by Jacques Gelinas
+ *
+ * Extended MS-DOS ioctl directory handling functions
+ */
+
+#include <asm/uaccess.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/umsdos_fs.h>
+
+struct UMSDOS_DIR_ONCE {
+ struct dirent *ent;
+ int count;
+};
+
+/*
+ * Record a single entry the first call.
+ * Return -EINVAL the next one.
+ */
+static int umsdos_ioctl_fill (
+ void *buf,
+ const char *name,
+ int name_len,
+ off_t offset,
+ ino_t ino)
+{
+ int ret = -EINVAL;
+ struct UMSDOS_DIR_ONCE *d = (struct UMSDOS_DIR_ONCE *) buf;
+
+ if (d->count == 0) {
+ copy_to_user (d->ent->d_name, name, name_len);
+ put_user ('\0', d->ent->d_name + name_len);
+ put_user (name_len, &d->ent->d_reclen);
+ put_user (ino, &d->ent->d_ino);
+ put_user (offset, &d->ent->d_off);
+ d->count = 1;
+ ret = 0;
+ }
+ return ret;
+}
+
+
+/*
+ * Perform special function on a directory
+ */
+/* #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 independently. These are essential to implement
+ * the synchronise.
+ */
+int UMSDOS_ioctl_dir(struct inode *dir, struct file *filp, unsigned int cmd,
+ unsigned long data_ptr)
+{
+ struct dentry *dentry = filp->f_dentry;
+ struct umsdos_ioctl *idata = (struct umsdos_ioctl *) data_ptr;
+ int ret;
+ struct file new_filp;
+ struct umsdos_ioctl data;
+
+ /* forward non-umsdos ioctls - this hopefully doesn't cause conflicts */
+ if (cmd != UMSDOS_GETVERSION
+ && cmd != UMSDOS_READDIR_DOS
+ && cmd != UMSDOS_READDIR_EMD
+ && cmd != UMSDOS_INIT_EMD
+ && cmd != UMSDOS_CREAT_EMD
+ && cmd != UMSDOS_RENAME_DOS
+ && cmd != UMSDOS_UNLINK_EMD
+ && cmd != UMSDOS_UNLINK_DOS
+ && cmd != UMSDOS_RMDIR_DOS
+ && cmd != UMSDOS_STAT_DOS
+ && cmd != UMSDOS_DOS_SETUP)
+ return fat_dir_ioctl (dir, filp, cmd, data_ptr);
+
+ /* #Specification: ioctl / acces
+ * Only root (effective id) is allowed to do IOCTL on directory
+ * in UMSDOS. EPERM is returned for other user.
+ */
+ /*
+ * Well, not all cases require write access, but it simplifies
+ * the code, and let's face it, there is only one client (umssync)
+ * for all this.
+ */
+ ret = verify_area (VERIFY_WRITE, (void *) data_ptr,
+ sizeof (struct umsdos_ioctl));
+ if (ret < 0)
+ goto out;
+
+ ret = -EPERM;
+ if (current->euid != 0 && cmd != UMSDOS_GETVERSION)
+ goto out;
+
+ ret = -EINVAL;
+ 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_user (UMSDOS_VERSION, &idata->version);
+ put_user (UMSDOS_RELEASE, &idata->release);
+ ret = 0;
+ goto out;
+ }
+ 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.
+ */
+ struct UMSDOS_DIR_ONCE bufk;
+
+ bufk.count = 0;
+ bufk.ent = &idata->dos_dirent;
+
+ fat_readdir (filp, &bufk, umsdos_ioctl_fill);
+
+ ret = bufk.count == 1 ? 1 : 0;
+ goto out;
+ }
+ 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 dentry *demd;
+
+ /* The absence of the EMD is simply seen as an EOF */
+ demd = umsdos_get_emd_dentry(dentry);
+ ret = PTR_ERR(demd);
+ if (IS_ERR(demd))
+ goto out;
+ fill_new_filp(&new_filp, demd);
+ new_filp.f_pos = filp->f_pos;
+
+ while (1) {
+ off_t f_pos = new_filp.f_pos;
+ struct umsdos_dirent entry;
+ struct umsdos_info info;
+
+ ret = 0;
+ if (new_filp.f_pos >= demd->d_inode->i_size)
+ break;
+
+ ret = umsdos_emd_dir_readentry (&new_filp, &entry);
+ if (ret < 0)
+ break;
+ if (entry.name_len <= 0)
+ continue;
+
+ umsdos_parse (entry.name, entry.name_len, &info);
+ info.f_pos = f_pos;
+ umsdos_manglename (&info);
+ ret = -EFAULT;
+ if (copy_to_user (&idata->umsdos_dirent, &entry,
+ sizeof (entry)))
+ break;
+ if (copy_to_user (&idata->dos_dirent.d_name,
+ info.fake.fname,
+ info.fake.len + 1))
+ break;
+ ret = entry.name_len;
+ break;
+ }
+ /* update the original f_pos */
+ filp->f_pos = new_filp.f_pos;
+ d_drop(demd);
+ dput(demd);
+ goto out;
+ }
+ if (cmd == UMSDOS_INIT_EMD) {
+ /* #Specification: ioctl / UMSDOS_INIT_EMD
+ * The UMSDOS_INIT_EMD command makes sure the EMD
+ * exists for a directory. If it does not, it is
+ * created. Also, it makes sure the directory function
+ * 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;
+
+ ret = umsdos_make_emd(dentry);
+ dir->i_op = (ret == 0)
+ ? &umsdos_dir_inode_operations
+ : &umsdos_rdir_inode_operations;
+ goto out;
+ }
+
+ ret = -EFAULT;
+ if (copy_from_user (&data, idata, sizeof (data)))
+ goto out;
+
+ 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 (dentry, &info);
+ goto out;
+ }
+ else if (cmd == UMSDOS_RENAME_DOS) {
+ struct dentry *old_dentry, *new_dentry; /* FIXME */
+
+ /* #Specification: ioctl / UMSDOS_RENAME_DOS
+ * A file or directory is renamed in a DOS directory
+ * (not moved across directory). The source name
+ * is in the dos_dirent.name field and the destination
+ * is in umsdos_dirent.name field.
+ *
+ * This ioctl allows umssync to rename a mangle file
+ * name before syncing it back in the EMD.
+ */
+ old_dentry = umsdos_lookup_dentry (dentry,
+ data.dos_dirent.d_name,
+ data.dos_dirent.d_reclen);
+ ret = PTR_ERR(old_dentry);
+ if (IS_ERR(old_dentry))
+ goto out;
+ new_dentry = umsdos_lookup_dentry (dentry,
+ data.umsdos_dirent.name,
+ data.umsdos_dirent.name_len);
+ ret = PTR_ERR(new_dentry);
+ if (!IS_ERR(new_dentry)) {
+printk("umsdos_ioctl: renaming %s/%s to %s/%s\n",
+old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
+new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
+ ret = msdos_rename (dir, old_dentry, dir, new_dentry);
+ dput(new_dentry);
+ }
+ d_drop(old_dentry);
+ dput(old_dentry);
+ goto out;
+ }
+ 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 (dentry, &info,
+ S_ISDIR (data.umsdos_dirent.mode));
+ goto out;
+ }
+ else if (cmd == UMSDOS_UNLINK_DOS) {
+ struct dentry *temp;
+
+ /* #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.
+ */
+ temp = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name,
+ data.dos_dirent.d_reclen);
+ ret = PTR_ERR(temp);
+ if (IS_ERR(temp))
+ goto out;
+ ret = -ENOENT;
+ if (temp->d_inode)
+ ret = msdos_unlink (dir, temp);
+ d_drop(temp);
+ dput (temp);
+ goto out;
+ }
+ else if (cmd == UMSDOS_RMDIR_DOS) {
+ struct dentry *temp;
+
+ /* #Specification: ioctl / UMSDOS_RMDIR_DOS
+ * The dos_dirent field of the struct umsdos_ioctl is used to
+ * execute a msdos_rmdir operation. The d_name and d_reclen
+ * fields are used.
+ *
+ * Return 0 if success.
+ */
+ temp = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name,
+ data.dos_dirent.d_reclen);
+ ret = PTR_ERR(temp);
+ if (IS_ERR(temp))
+ goto out;
+ ret = -ENOENT;
+ if (temp->d_inode)
+ ret = msdos_rmdir (dir, temp);
+ d_drop(temp);
+ dput (temp);
+ goto out;
+
+ } 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 dentry *dret;
+ struct inode *inode;
+
+ dret = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name,
+ data.dos_dirent.d_reclen);
+ ret = PTR_ERR(dret);
+ if (IS_ERR(dret))
+ goto out;
+ ret = -ENOENT;
+ inode = dret->d_inode;
+ if (inode) {
+ 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;
+ ret = -EFAULT;
+ if (!copy_to_user (&idata->stat, &data.stat,
+ sizeof (data.stat)))
+ ret = 0;
+ }
+ d_drop(dret);
+ dput(dret);
+ goto out;
+ }
+ else if (cmd == UMSDOS_DOS_SETUP) {
+ /* #Specification: ioctl / UMSDOS_DOS_SETUP
+ * The UMSDOS_DOS_SETUP ioctl allow changing the
+ * default permission of the MS-DOS filesystem driver
+ * on the fly. The MS-DOS driver applies global permissions
+ * 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 applies ONLY to MS-DOS directories
+ * (those without EMD --linux-.---). Umsdos directory
+ * have independent (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.options.fs_uid = data.umsdos_dirent.uid;
+ dir->i_sb->u.msdos_sb.options.fs_gid = data.umsdos_dirent.gid;
+ dir->i_sb->u.msdos_sb.options.fs_umask = data.umsdos_dirent.mode;
+ ret = 0;
+ }
+out:
+ Printk (("ioctl %d, returning %d\n", cmd, ret));
+ return ret;
+}
#include <linux/kernel.h>
#include <linux/umsdos_fs.h>
+/* (This file is used outside of the kernel) */
+#ifndef __KERNEL__
+#define KERN_WARNING
+#endif
+
/*
* Complete the mangling of the MSDOS fake name
* based on the position of the entry in the EMD file.
-/*
- * 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>
-
-#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 recursively 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++;
-}
-
-/*
- * 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,
- struct dentry *dentry,
- int errcod)
-{ /* Length of the name */
- const char *name = dentry->d_name.name;
- int len = dentry->d_name.len;
- int ret = 0;
-
- if (umsdos_is_pseudodos (dir, dentry)) {
- /* #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 successful, 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,
- struct dentry *dentry, /* name/length etc */
- int mode, /* Permission bit + file type ??? */
- int rdev, /* major, minor or 0 for ordinary file and symlinks */
- char flags
-)
-{ /* Will hold the inode of the newly created file */
-
- int ret;
- struct dentry *fake;
-
- Printk (("umsdos_create_any /mn/: create %.*s in dir=%lu - nevercreat=/", (int) dentry->d_name.len, dentry->d_name.name, dir->i_ino));
- check_dentry_path (dentry, "umsdos_create_any");
- ret = umsdos_nevercreat (dir, dentry, -EEXIST);
- Printk (("%d/\n", ret));
- if (ret == 0) {
- struct umsdos_info info;
-
- ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
-
- if (ret == 0) {
- info.entry.mode = mode;
- info.entry.rdev = rdev;
- info.entry.flags = flags;
- info.entry.uid = current->fsuid;
- info.entry.gid = (dir->i_mode & S_ISGID)
- ? dir->i_gid : current->fsgid;
- 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) {
- inc_count (dir);
- fake = creat_dentry (info.fake.fname, info.fake.len, NULL, dentry->d_parent); /* create short name dentry */
- ret = msdos_create (dir, fake, S_IFREG | 0777);
- if (ret == 0) {
- struct inode *inode = fake->d_inode;
-
- umsdos_lookup_patch (dir, inode, &info.entry, info.f_pos);
- Printk (("inode %p[%lu], count=%d ", inode, inode->i_ino, inode->i_count));
- Printk (("Creation OK: [dir %lu] %.*s pid=%d pos %ld\n", dir->i_ino,
- info.fake.len, info.fake.fname, current->pid, info.f_pos));
-
- check_dentry_path (dentry, "umsdos_create_any: BEG dentry");
- check_dentry_path (fake, "umsdos_create_any: BEG fake");
- d_instantiate (dentry, inode); /* long name also gets inode info */
- inc_count (fake->d_inode); /* we must do it, since dput(fake) will iput(our_inode) which we still need for long name (dentry) */
- /* dput (fake); / * FIXME: is this OK ? we try to kill short name dentry that we don't need */
- check_dentry_path (dentry, "umsdos_create_any: END dentry");
- check_dentry_path (fake, "umsdos_create_any: END fake");
-
- } 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.len, info.fake.fname, -ret, current->pid, info.f_pos);
- }
- umsdos_delentry (dir, &info, 0);
- }
- Printk (("umsdos_create %.*s ret = %d pos %ld\n",
- info.fake.len, info.fake.fname, ret, info.f_pos));
- }
- umsdos_unlockcreate (dir);
- }
- }
- /* d_add(dentry,dir); /mn/ FIXME: msdos_create already did this for short name ! */
- 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__); \
-}
-
-#undef chkstk
-#define chkstk() do { } while (0);
-
-/*
- * Rename a file (move) in the file system.
- */
-
-static int umsdos_rename_f (
- struct inode *old_dir,
- struct dentry *old_dentry,
- struct inode *new_dir,
- struct dentry *new_dentry,
- 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_dentry->d_name.name, old_dentry->d_name.len, &old_info);
- struct umsdos_info new_info;
- int new_ret = umsdos_parse (new_dentry->d_name.name, new_dentry->d_name.len, &new_info);
-
- check_dentry_path (old_dentry, "umsdos_rename_f OLD");
- check_dentry_path (new_dentry, "umsdos_rename_f OLD");
-
- 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) {
- /* check sticky bit on old_dir */
- if (!(old_dir->i_mode & S_ISVTX) || capable (CAP_FOWNER) ||
- current->fsuid == old_info.entry.uid ||
- current->fsuid == old_dir->i_uid) {
- /* Does new_name already exist? */
- Printk (("new findentry "));
- ret = umsdos_findentry (new_dir, &new_info, 0);
- if (ret != 0 || /* if destination file exists, are we allowed to replace it ? */
- !(new_dir->i_mode & S_ISVTX) || capable (CAP_FOWNER) ||
- current->fsuid == new_info.entry.uid ||
- current->fsuid == new_dir->i_uid) {
- 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) {
- struct dentry *old, *new, *d_old_dir, *dret;
- struct inode *oldid = NULL;
-
- d_old_dir = creat_dentry ("@d_old_dir@", 11, old_dir, NULL);
- dret = compat_umsdos_real_lookup (d_old_dir, old_info.fake.fname, old_info.fake.len);
- if (dret) oldid = dret->d_inode;
- old = creat_dentry (old_info.fake.fname, old_info.fake.len, oldid, old_dentry->d_parent);
-
- new = creat_dentry (new_info.fake.fname, new_info.fake.len, NULL, new_dentry->d_parent);
-
- Printk (("msdos_rename "));
- inc_count (old_dir);
- inc_count (new_dir); /* Both inode are needed later */
-
- check_dentry_path (old, "umsdos_rename_f OLD2");
- check_dentry_path (new, "umsdos_rename_f NEW2");
-
- ret = msdos_rename (old_dir, old, new_dir, new);
- chkstk ();
- Printk (("after m_rename ret %d ", ret));
- kill_dentry (old);
- kill_dentry (new);
-
- 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_x 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;
-
- inc_count (new_dir);
- PRINTK ((KERN_DEBUG "rename lookup len %d %d -- ", new_len, new_info.entry.flags));
- ret = umsdos_lookup_x (new_dir, new_dentry, 0);
- inode = new_dentry->d_inode;
- chkstk ();
- if (ret != 0) {
- printk ("UMSDOS: partial rename for file %.*s\n"
- ,new_info.entry.name_len, 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);
- d_move (old_dentry, new_dentry);
- chkstk ();
- /* iput (inode); FIXME */
- }
- }
- fin_dentry (dret);
- }
- }
- } else {
- /* sticky bit set on new_dir */
- Printk (("sticky set on new "));
- ret = -EPERM;
- }
- } else {
- /* sticky bit set on old_dir */
- Printk (("sticky set on old "));
- ret = -EPERM;
- }
- }
- Printk ((KERN_DEBUG "umsdos_rename_f: unlocking dirs...\n"));
- umsdos_unlockcreate (old_dir);
- umsdos_unlockcreate (new_dir);
- }
- check_dentry_path (old_dentry, "umsdos_rename_f OLD3");
- check_dentry_path (new_dentry, "umsdos_rename_f NEW3");
-
- Printk ((" _ret=%d\n", ret));
- 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,
- struct dentry *dentry,
- 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.
- *
- * Let's go for simplicity...
- */
-
-
- int ret;
-
- inc_count (dir); /* We keep the inode in case we need it */
- /* later */
- ret = umsdos_create_any (dir, dentry, mode, 0, flags);
- Printk (("umsdos_symlink ret %d ", ret));
- if (ret == 0) {
- int len = strlen (symname);
- struct file filp;
-
- fill_new_filp (&filp, dentry);
- filp.f_pos = 0;
-
- /* Make the inode acceptable to MSDOS FIXME */
- Printk ((KERN_WARNING "umsdos_symlink_x: /mn/ is this OK?\n"));
- Printk ((KERN_WARNING " symname=%s ; dentry name=%.*s (ino=%lu)\n", symname, (int) dentry->d_name.len, dentry->d_name.name, dentry->d_inode->i_ino));
- ret = umsdos_file_write_kmem_real (&filp, symname, len);
- /* dput(dentry); ?? where did this come from FIXME */
-
- 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, dentry);
- dir = NULL;
- }
- }
- /* d_instantiate(dentry,dir); //already done in umsdos_create_any. */
- Printk (("\n"));
- return ret;
-}
-
-/*
- * Setup un Symbolic link.
- * Return a negative error code or 0 if OK.
- */
-int UMSDOS_symlink (
- struct inode *dir,
- struct dentry *dentry,
- const char *symname
-)
-{
- return umsdos_symlink_x (dir, dentry, symname, S_IFLNK | 0777, 0);
-}
-
-/*
- * Add a link to an inode in a directory
- */
-int UMSDOS_link (
- struct dentry *olddentry,
- struct inode *dir,
- struct dentry *dentry)
-{
- struct inode *oldinode = olddentry->d_inode;
-
- /* #Specification: hard link / strategy
- * Hard links are difficult to implement on top of an MS-DOS FAT file
- * system. Unlike Unix file systems, there are no inodes. A directory
- * entry holds the functionality of the inode and the entry.
- *
- * We will used the same strategy as a normal Unix file system
- * (with inodes) except we will do it symbolically (using paths).
- *
- * Because anything can happen during a DOS session (defragment,
- * directory sorting, etc.), we can't rely on an MS-DOS pseudo
- * inode number to record the link. For this reason, the link
- * will be done using hidden symbolic links. The following
- * scenario illustrates how it works.
- *
- * 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 or /tmp/file2 independently.
- *
- * 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, dentry, -EPERM)) == 0) {
- struct inode *olddir;
-
- ret = umsdos_get_dirowner (oldinode, &olddir);
- Printk (("umsdos_link dir_owner = %lu -> %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 %lu flags %d "
- ,entry.name_len, 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
- * symbolic 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) {
- Printk (("olddir[%d] ", olddir->i_count));
- ret = umsdos_rename_f (olddentry->d_inode, olddentry, dir, dentry, UMSDOS_HIDDEN);
- if (ret == 0) {
- char *path = (char *) kmalloc (PATH_MAX, GFP_KERNEL);
-
- if (path == NULL) {
- ret = -ENOMEM;
- } else {
- struct dentry *temp;
-
- temp = creat_dentry (entry.name, entry.name_len, NULL, NULL);
- Printk (("olddir[%d] ", olddir->i_count));
- ret = umsdos_locate_path (oldinode, path);
- Printk (("olddir[%d] ", olddir->i_count));
- if (ret == 0) {
- inc_count (olddir);
- ret = umsdos_symlink_x (olddir, temp, path, S_IFREG | 0777, UMSDOS_HLINK);
- if (ret == 0) {
- inc_count (dir);
- ret = umsdos_symlink_x (dir, dentry, 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) {
- inc_count (dir);
- ret = umsdos_symlink_x (dir, dentry, path, S_IFREG | 0777, UMSDOS_HLINK);
- }
- kfree (path);
- }
- }
- }
- umsdos_unlockcreate (olddir);
- umsdos_unlockcreate (dir);
- }
- /* iput (olddir); FIXME */
- }
- if (ret == 0) {
- struct iattr newattrs;
-
- oldinode->i_nlink++;
- newattrs.ia_valid = 0;
- ret = UMSDOS_notify_change (olddentry, &newattrs);
- }
-/* dput (olddentry);
- * dput (dentry); FIXME.... */
-
- 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 successful, it
- * is then added to the EDM file.
- *
- * Return the status of the operation. 0 mean success.
- */
-int UMSDOS_create (
- struct inode *dir,
- struct dentry *dentry,
- int mode /* Permission bit + file type ??? */
-)
-{ /* Will hold the inode of the newly created file */
- int ret;
- Printk ((KERN_ERR "UMSDOS_create: entering\n"));
- check_dentry_path (dentry, "UMSDOS_create START");
- ret = umsdos_create_any (dir, dentry, mode, 0, 0);
- check_dentry_path (dentry, "UMSDOS_create END");
- return ret;
-}
-
-
-
-/*
- * Add a sub-directory in a directory
- */
-int UMSDOS_mkdir (
- struct inode *dir,
- struct dentry *dentry,
- int mode)
-{
- int ret = umsdos_nevercreat (dir, dentry, -EEXIST);
-
- if (ret == 0) {
- struct umsdos_info info;
-
- ret = umsdos_parse (dentry->d_name.name, dentry->d_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->fsuid;
- info.entry.gid = (dir->i_mode & S_ISGID)
- ? dir->i_gid : current->fsgid;
- 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) {
- struct dentry *temp, *tdir;
-
- tdir = creat_dentry ("@mkd-dir@", 9, dir, NULL);
- temp = creat_dentry (info.fake.fname, info.fake.len, NULL, tdir);
- inc_count (dir);
- ret = msdos_mkdir (dir, temp, 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 immediately an EMD file in the new
- * sub-directory so it inherit UMSDOS semantic.
- */
- struct inode *subdir=NULL;
- struct dentry *d_dir, *dret;
-
- d_dir = creat_dentry ("@d_dir5@", 7, dir, NULL);
- dret = compat_umsdos_real_lookup (d_dir, info.fake.fname, info.fake.len);
- if (dret) {
- struct dentry *tdentry, *tdsub;
-
- subdir = dret->d_inode;
-
- tdsub = creat_dentry ("@mkd-emd@", 9, subdir, NULL);
- tdentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL, tdsub);
- ret = msdos_create (subdir, tdentry, S_IFREG | 0777);
- kill_dentry (tdentry); /* we don't want empty EMD file to be visible ! too bad kill_dentry does nothing at the moment :-) FIXME */
- kill_dentry (tdsub);
- umsdos_setup_dir_inode (subdir); /* this should setup dir so it is promoted to EMD, and EMD file is not visible inside it */
- subdir = NULL;
- d_instantiate (dentry, temp->d_inode);
- /* iput (result); FIXME */
- fin_dentry (dret);
- }
- if (ret < 0) {
- printk ("UMSDOS: Can't create empty --linux-.---\n");
- }
- /*iput (subdir); FIXME*/
- }
- }
- umsdos_unlockcreate (dir);
- }
- }
- Printk (("umsdos_mkdir %d\n", ret));
- /* dput (dentry); / * FIXME /mn/ */
- return ret;
-}
-
-/*
- * Add a new device special file into a directory.
- */
-int UMSDOS_mknod (
- struct inode *dir,
- struct dentry *dentry,
- 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 those 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.
- */
-
- int ret;
-
- check_dentry_path (dentry, "UMSDOS_mknod START");
- ret = umsdos_create_any (dir, dentry, mode, rdev, 0);
- check_dentry_path (dentry, "UMSDOS_mknod END");
-
- /* dput(dentry); / * /mn/ FIXME! */
- return ret;
-}
-
-/*
- * Remove a sub-directory.
- */
-int UMSDOS_rmdir (
- struct inode *dir,
- struct dentry *dentry)
-{
- /* #Specification: style / iput strategy
- * In the UMSDOS project, I am trying to apply a single
- * programming style regarding inode management. Many
- * entry points are receiving an inode to act on, and must
- * do an iput() as soon as they are finished with
- * the inode.
- *
- * For simple cases, there is no problem. When you introduce
- * error checking, you end up with many iput() calls in the
- * code.
- *
- * The coding style I use all around is one where I am trying
- * to provide independent flow logic (I don't know how to
- * name this). With this style, code is easier to understand
- * but you must do iput() everywhere. Here is an example
- * of what I am trying to avoid.
- *
- * #
- * if (a){
- * ...
- * if(b){
- * ...
- * }
- * ...
- * if (c){
- * // Complex state. Was b true?
- * ...
- * }
- * ...
- * }
- * // Weird state
- * if (d){
- * // ...
- * }
- * // Was iput finally done?
- * return status;
- * #
- *
- * Here is the style I am using. Still sometimes 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
- * pair goes against this practice of "forgetting" the inode as soon
- * as possible.
- */
-
- int ret;
-
- ret = umsdos_nevercreat (dir, dentry, -EPERM);
- if (ret == 0) {
- /* volatile - DELME: I see no reason vor volatile /mn/ */ struct inode *sdir;
-
- inc_count (dir);
- ret = umsdos_lookup_x (dir, dentry, 0);
- sdir = dentry->d_inode;
- Printk (("rmdir lookup %d ", ret));
- if (ret == 0) {
- int empty;
-
- umsdos_lockcreate (dir);
-
- Printk ((" /mn/ rmdir: FIXME EBUSY TEST: hmmm, i_count is %d > 1 -- FAKING!\n", sdir->i_count));
- sdir->i_count = 1; /* /mn/ FIXME! DELME! FOR TEST ONLY ! */
-
- if (sdir->i_count > 1) {
- Printk ((" /mn/ rmdir: FIXME EBUSY: hmmm, i_count is %d > 1 -- FAKING!\n", sdir->i_count));
- ret = -EBUSY;
- } else if ((empty = umsdos_isempty (sdir)) != 0) {
- struct dentry *tdentry, *tedir;
-
- tedir = creat_dentry ("@emd-rmd@", 9, dir, NULL);
- tdentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL, tedir);
- umsdos_real_lookup (dir, tdentry); /* fill inode part */
- Printk (("isempty %d i_count %d ", empty, sdir->i_count));
- /* check sticky bit */
- if (!(dir->i_mode & S_ISVTX) || capable (CAP_FOWNER) ||
- current->fsuid == sdir->i_uid ||
- current->fsuid == dir->i_uid) {
- if (empty == 1) {
- /* We have to remove the EMD file */
- ret = msdos_unlink (sdir, tdentry);
- Printk (("UMSDOS_rmdir: unlinking empty EMD ret=%d", ret));
- sdir = NULL;
- }
- /* sdir must be free before msdos_rmdir() */
- /* iput (sdir); FIXME */
- sdir = NULL;
- Printk (("isempty ret %d nlink %d ", ret, dir->i_nlink));
- if (ret == 0) {
- struct umsdos_info info;
- struct dentry *temp, *tdir;
-
- inc_count (dir);
- umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
- /* The findentry is there only to complete */
- /* the mangling */
- umsdos_findentry (dir, &info, 2);
-
- tdir = creat_dentry ("@dir-rmd@", 9, dir, NULL);
- temp = creat_dentry (info.fake.fname, info.fake.len, NULL, tdir);
- umsdos_real_lookup (dir, temp); /* fill inode part */
-
- Printk ((KERN_ERR " rmdir start dir=%lu, dir->sb=%p\n", dir->i_ino, dir->i_sb)); /* FIXME: /mn/ debug only */
- Printk ((KERN_ERR " dentry=%.*s d_count=%d ino=%lu\n", (int) temp->d_name.len, temp->d_name.name, temp->d_count, temp->d_inode->i_ino));
- Printk ((KERN_ERR " d_parent=%.*s d_count=%d ino=%lu\n", (int) temp->d_parent->d_name.len, temp->d_parent->d_name.name, temp->d_parent->d_count, temp->d_parent->d_inode->i_ino));
-
- ret = msdos_rmdir (dir, temp);
-
- Printk ((KERN_ERR " rmdir passed %d\n", ret)); /* FIXME: /mn/ debug only */
- Printk ((KERN_ERR " rmdir end dir=%lu, dir->sb=%p\n", dir->i_ino, dir->i_sb));
- Printk ((KERN_ERR " dentry=%.*s d_count=%d ino=%p\n", (int) temp->d_name.len, temp->d_name.name, temp->d_count, temp->d_inode));
- Printk ((KERN_ERR " d_parent=%.*s d_count=%d ino=%lu\n", (int) temp->d_parent->d_name.len, temp->d_parent->d_name.name, temp->d_parent->d_count, temp->d_parent->d_inode->i_ino));
-
- kill_dentry (tdir);
- kill_dentry (temp);
-
- if (ret == 0) {
- ret = umsdos_delentry (dir, &info, 1);
- d_delete (dentry);
- }
- }
- } else {
- /* sticky bit set and we don't have permission */
- Printk (("sticky set "));
- ret = -EPERM;
- }
- } else {
- /*
- * The subdirectory is not empty, so leave it there
- */
- ret = -ENOTEMPTY;
- }
- /* iput(sdir); FIXME */
- umsdos_unlockcreate (dir);
- }
- }
- /* dput(dentry); FIXME /mn/ */
- Printk (("umsdos_rmdir %d\n", ret));
- return ret;
-}
-
-
-
-/*
- * Remove a file from the directory.
- */
-int UMSDOS_unlink (
- struct inode *dir,
- struct dentry *dentry)
-{
- int ret;
-
- Printk ((" *** UMSDOS_unlink entering /mn/ *** \n"));
-
- ret = umsdos_nevercreat (dir, dentry, -EPERM);
-
- Printk (("UMSDOS_unlink /mn/: nevercreat=%d\n", ret));
-
- if (ret == 0) {
- struct umsdos_info info;
-
- ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
- if (ret == 0) {
- umsdos_lockcreate (dir);
- ret = umsdos_findentry (dir, &info, 1);
- Printk (("UMSDOS_unlink: findentry returned %d\n", ret));
- if (ret == 0) {
- Printk (("UMSDOS_unlink %.*s ", info.fake.len, info.fake.fname));
- /* check sticky bit */
- if (!(dir->i_mode & S_ISVTX) || capable (CAP_FOWNER) ||
- current->fsuid == info.entry.uid ||
- current->fsuid == dir->i_uid) {
- if (info.entry.flags & UMSDOS_HLINK) {
- /* #Specification: hard link / deleting a link
- * When we delete a file, and this file is a link
- * we must subtract 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;
-
- inc_count (dir);
- ret = umsdos_lookup_x (dir, dentry, 0);
- inode = dentry->d_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, dentry);
- } else {
- /* iput (hdir); FIXME */
- }
- } else {
- struct iattr newattrs;
-
- newattrs.ia_valid = 0;
- ret = UMSDOS_notify_change (dentry, &newattrs);
- }
- /* iput (inode); FIXME */
- }
- }
- if (ret == 0) {
- ret = umsdos_delentry (dir, &info, 0);
- if (ret == 0) {
- struct dentry *temp,
- *tdir;
-
- Printk (("Before msdos_unlink %.*s ", info.fake.len, info.fake.fname));
- /* inc_count (dir); / * FIXME /mn/ is this needed any more now that msdos_unlink locks directories using d_parent ? */
- tdir = creat_dentry ("@dir-del@", 9, dir, NULL); /* FIXME /mn/: do we need iget(dir->i_ino) or would dir itself suffice ? */
- temp = creat_dentry (info.fake.fname, info.fake.len, NULL, tdir);
- umsdos_real_lookup (dir, temp); /* fill inode part */
-
- ret = msdos_unlink_umsdos (dir, temp);
- Printk (("msdos_unlink %.*s %o ret %d ", info.fake.len, info.fake.fname
- ,info.entry.mode, ret));
-
- d_delete (dentry);
-
- kill_dentry (tdir);
- kill_dentry (temp);
- }
- }
- } else {
- /* sticky bit set and we've not got permission */
- Printk (("Sticky bit set. "));
- ret = -EPERM;
- }
- }
- umsdos_unlockcreate (dir);
- }
- }
- /* dput(dentry); FIXME: shouldn't this be done in msdos_unlink ? */
- Printk (("umsdos_unlink %d\n", ret));
- return ret;
-}
-
-
-
-/*
- * Rename (move) a file.
- */
-int UMSDOS_rename (
- struct inode *old_dir,
- struct dentry *old_dentry,
- struct inode *new_dir,
- struct dentry *new_dentry)
-{
- /* #Specification: weakness / rename
- * There is a case where UMSDOS rename has a different behavior
- * than a 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 may is in Linux VFS driver for msdos.
- * I believe this is not a bug but a design feature, because
- * an inode number represents 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_dentry, -EEXIST);
-
- if (ret == 0) {
- /* umsdos_rename_f eat the inode and we may need those later */
- inc_count (old_dir);
- inc_count (new_dir);
- ret = umsdos_rename_f (old_dir, old_dentry, new_dir, new_dentry, 0);
- if (ret == -EEXIST) {
- /* #Specification: rename / new name exist
- * If the destination name already exists, 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 ensuring that only
- * one process could achieve a rename (modification) operation
- * in the source and destination directory. This was also
- * ensuring the operation was "atomic".
- *
- * This has been changed because this was creating a
- * stack overflow (the stack is only 4 kB) 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 may 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 terribly efficient but should work. */
- inc_count (new_dir);
- ret = UMSDOS_unlink (new_dir, new_dentry);
- chkstk ();
- Printk (("rename unlink ret %d -- ", ret));
- if (ret == -EISDIR) {
- inc_count (new_dir);
- ret = UMSDOS_rmdir (new_dir, new_dentry);
- chkstk ();
- Printk (("rename rmdir ret %d -- ", ret));
- }
- if (ret == 0) {
- ret = umsdos_rename_f (old_dir, old_dentry,
- new_dir, new_dentry, 0);
- new_dir = old_dir = NULL;
- }
- }
- }
- /*
- * dput (new_dentry);
- * dput (old_dentry); FIXME /mn/ */
- return ret;
-}
+/*
+ * 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>
+
+#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.
+ */
+/* #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 recursively 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.
+ */
+void umsdos_lockcreate (struct inode *dir)
+{
+ /*
+ * 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++;
+}
+
+/*
+ * 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
+
+/*
+ * Check whether we can delete from the directory.
+ */
+static int is_sticky(struct inode *dir, int uid)
+{
+ return !((dir->i_mode & S_ISVTX) == 0 ||
+ capable (CAP_FOWNER) ||
+ current->fsuid == uid ||
+ current->fsuid == dir->i_uid);
+}
+
+
+static int umsdos_nevercreat (struct inode *dir, struct dentry *dentry,
+ int errcod)
+{
+ const char *name = dentry->d_name.name;
+ int len = dentry->d_name.len;
+ int ret = 0;
+
+ if (umsdos_is_pseudodos (dir, dentry)) {
+ /* #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 successful, it
+ * is then added to the EMD file.
+ *
+ * Return the status of the operation. 0 mean success.
+ *
+ * #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
+ * exists 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 meant 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.
+ */
+static int umsdos_create_any (struct inode *dir, struct dentry *dentry,
+ int mode, int rdev, char flags)
+{
+ struct dentry *fake;
+ struct inode *inode;
+ int ret;
+ struct umsdos_info info;
+
+if (dentry->d_inode)
+printk("umsdos_create_any: %s/%s not negative!\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+
+Printk (("umsdos_create_any /mn/: create %.*s in dir=%lu - nevercreat=/",
+(int) dentry->d_name.len, dentry->d_name.name, dir->i_ino));
+
+ check_dentry_path (dentry, "umsdos_create_any");
+ ret = umsdos_nevercreat (dir, dentry, -EEXIST);
+ if (ret) {
+Printk (("%d/\n", ret));
+ goto out;
+ }
+
+ ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
+ if (ret)
+ goto out;
+
+ info.entry.mode = mode;
+ info.entry.rdev = rdev;
+ info.entry.flags = flags;
+ info.entry.uid = current->fsuid;
+ info.entry.gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
+ info.entry.ctime = info.entry.atime = info.entry.mtime = CURRENT_TIME;
+ info.entry.nlink = 1;
+ umsdos_lockcreate (dir);
+ ret = umsdos_newentry (dentry->d_parent, &info);
+ if (ret)
+ goto out_unlock;
+
+ /* create short name dentry */
+ fake = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname,
+ info.fake.len);
+ ret = PTR_ERR(fake);
+ if (IS_ERR(fake))
+ goto out_unlock;
+
+ /* should not exist yet ... */
+ ret = -EEXIST;
+ if (fake->d_inode)
+ goto out_remove;
+
+ ret = msdos_create (dir, fake, S_IFREG | 0777);
+ if (ret)
+ goto out_remove;
+
+ inode = fake->d_inode;
+ umsdos_lookup_patch_new(fake, &info.entry, info.f_pos);
+
+Printk (("inode %p[%lu], count=%d ", inode, inode->i_ino, inode->i_count));
+Printk (("Creation OK: [dir %lu] %.*s pid=%d pos %ld\n",
+dir->i_ino, info.fake.len, info.fake.fname, current->pid, info.f_pos));
+
+ check_dentry_path (dentry, "umsdos_create_any: BEG dentry");
+ check_dentry_path (fake, "umsdos_create_any: BEG fake");
+
+ /*
+ * Note! The long and short name might be the same,
+ * so check first before doing the instantiate ...
+ */
+ if (dentry != fake) {
+ /* long name also gets inode info */
+ inode->i_count++;
+ d_instantiate (dentry, inode);
+ }
+
+ check_dentry_path (dentry, "umsdos_create_any: END dentry");
+ check_dentry_path (fake, "umsdos_create_any: END fake");
+ goto out_dput;
+
+out_remove:
+if (ret == -EEXIST)
+printk("UMSDOS: out of sync, error [%ld], deleting %.*s %d %d pos %ld\n",
+dir->i_ino ,info.fake.len, info.fake.fname, -ret, current->pid, info.f_pos);
+ umsdos_delentry (dentry->d_parent, &info, 0);
+
+out_dput:
+Printk (("umsdos_create %.*s ret = %d pos %ld\n",
+info.fake.len, info.fake.fname, ret, info.f_pos));
+ /* N.B. any value in keeping short name dentries? */
+ if (dentry != fake)
+ d_drop(fake);
+ dput(fake);
+
+out_unlock:
+ umsdos_unlockcreate (dir);
+out:
+ 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, 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__); \
+}
+
+#undef chkstk
+#define chkstk() do { } while (0);
+
+/*
+ * Rename a file (move) in the file system.
+ */
+
+static int umsdos_rename_f (struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry,
+ int flags)
+{
+ int old_ret, new_ret;
+ struct dentry *old, *new, *dret;
+ struct inode *oldid = NULL;
+ int ret = -EPERM;
+ struct umsdos_info old_info;
+ struct umsdos_info new_info;
+
+ old_ret = umsdos_parse (old_dentry->d_name.name,
+ old_dentry->d_name.len, &old_info);
+ if (old_ret)
+ goto out;
+ new_ret = umsdos_parse (new_dentry->d_name.name,
+ new_dentry->d_name.len, &new_info);
+ if (new_ret)
+ goto out;
+
+ check_dentry_path (old_dentry, "umsdos_rename_f OLD");
+ check_dentry_path (new_dentry, "umsdos_rename_f OLD");
+
+ chkstk ();
+Printk (("umsdos_rename %d %d ", old_ret, new_ret));
+ umsdos_lockcreate2 (old_dir, new_dir);
+ chkstk ();
+
+ ret = umsdos_findentry(old_dentry->d_parent, &old_info, 0);
+ chkstk ();
+ if (ret) {
+Printk (("ret %d ", ret));
+ goto out_unlock;
+ }
+
+ /* check sticky bit on old_dir */
+ ret = -EPERM;
+ if (is_sticky(old_dir, old_info.entry.uid)) {
+ Printk (("sticky set on old "));
+ goto out_unlock;
+ }
+
+ /* Does new_name already exist? */
+ new_ret = umsdos_findentry(new_dentry->d_parent, &new_info, 0);
+ /* if destination file exists, are we allowed to replace it ? */
+ if (new_ret == 0 && is_sticky(new_dir, new_info.entry.uid)) {
+ Printk (("sticky set on new "));
+ goto out_unlock;
+ }
+
+ Printk (("new newentry "));
+ umsdos_ren_init (&new_info, &old_info, flags);
+ ret = umsdos_newentry (new_dentry->d_parent, &new_info);
+ chkstk ();
+ if (ret) {
+Printk (("ret %d %d ", ret, new_info.fake.len));
+ goto out_unlock;
+ }
+
+ dret = umsdos_lookup_dentry(old_dentry->d_parent, old_info.fake.fname,
+ old_info.fake.len);
+ ret = PTR_ERR(dret);
+ if (IS_ERR(dret))
+ goto out_unlock;
+#if 0
+ /* This is the same as dret */
+ oldid = dret->d_inode;
+ old = creat_dentry (old_info.fake.fname, old_info.fake.len,
+ oldid, old_dentry->d_parent);
+#endif
+ old = dret;
+ new = umsdos_lookup_dentry(new_dentry->d_parent, new_info.fake.fname,
+ new_info.fake.len);
+ ret = PTR_ERR(new);
+ if (IS_ERR(new))
+ goto out_dput;
+
+ Printk (("msdos_rename "));
+ check_dentry_path (old, "umsdos_rename_f OLD2");
+ check_dentry_path (new, "umsdos_rename_f NEW2");
+ ret = msdos_rename (old_dir, old, new_dir, new);
+ chkstk ();
+printk("after m_rename ret %d ", ret);
+ /* dput(old); */
+ dput(new);
+
+ if (ret != 0) {
+ umsdos_delentry (new_dentry->d_parent, &new_info,
+ S_ISDIR (new_info.entry.mode));
+ chkstk ();
+ goto out_dput;
+ }
+
+ ret = umsdos_delentry (old_dentry->d_parent, &old_info,
+ S_ISDIR (old_info.entry.mode));
+ chkstk ();
+ if (ret)
+ goto out_dput;
+#if 0
+ /*
+ * Update f_pos so notify_change will succeed
+ * if the file was already in use.
+ */
+ umsdos_set_dirinfo (new_dentry->d_inode, new_dir, new_info.f_pos);
+#endif
+ if (old_dentry == dret) {
+printk("umsdos_rename_f: old dentries match -- skipping d_move\n");
+ goto out_dput;
+ }
+ d_move (old_dentry, new_dentry);
+
+out_dput:
+ dput(dret);
+
+out_unlock:
+ Printk ((KERN_DEBUG "umsdos_rename_f: unlocking dirs...\n"));
+ umsdos_unlockcreate (old_dir);
+ umsdos_unlockcreate (new_dir);
+
+out:
+ check_dentry_path (old_dentry, "umsdos_rename_f OLD3");
+ check_dentry_path (new_dentry, "umsdos_rename_f NEW3");
+ Printk ((" _ret=%d\n", ret));
+ return ret;
+}
+
+/*
+ * Setup a Symbolic link or a (pseudo) hard link
+ * Return a negative error code or 0 if OK.
+ */
+/* #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.
+ *
+ * Let's go for simplicity...
+ */
+
+static int umsdos_symlink_x (struct inode *dir, struct dentry *dentry,
+ const char *symname, int mode, char flags)
+{
+ int ret, len;
+ struct file filp;
+
+ ret = umsdos_create_any (dir, dentry, mode, 0, flags);
+ if (ret) {
+Printk (("umsdos_symlink ret %d ", ret));
+ goto out;
+ }
+
+ len = strlen (symname);
+
+ fill_new_filp (&filp, dentry);
+ filp.f_pos = 0;
+
+ /* Make the inode acceptable to MSDOS FIXME */
+Printk ((KERN_WARNING " symname=%s ; dentry name=%.*s (ino=%lu)\n",
+symname, (int) dentry->d_name.len, dentry->d_name.name, dentry->d_inode->i_ino));
+ ret = umsdos_file_write_kmem_real (&filp, symname, len);
+
+ 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, dentry);
+ }
+
+out:
+ Printk (("\n"));
+ return ret;
+}
+
+/*
+ * Setup a Symbolic link.
+ * Return a negative error code or 0 if OK.
+ */
+int UMSDOS_symlink ( struct inode *dir, struct dentry *dentry,
+ const char *symname)
+{
+ return umsdos_symlink_x (dir, dentry, symname, S_IFLNK | 0777, 0);
+}
+
+/*
+ * Add a link to an inode in a directory
+ */
+int UMSDOS_link (struct dentry *olddentry, struct inode *dir,
+ struct dentry *dentry)
+{
+ struct inode *oldinode = olddentry->d_inode;
+ struct inode *olddir;
+ char *path;
+ struct dentry *temp;
+ unsigned long buffer;
+ int ret;
+ struct umsdos_dirent entry;
+
+ ret = -EPERM;
+ if (S_ISDIR (oldinode->i_mode))
+ goto out;
+
+ ret = umsdos_nevercreat (dir, dentry, -EPERM);
+ if (ret)
+ goto out;
+
+ ret = -ENOMEM;
+ buffer = get_free_page(GFP_KERNEL);
+ if (!buffer)
+ goto out;
+
+ olddir = olddentry->d_parent->d_inode;
+ umsdos_lockcreate2 (dir, olddir);
+
+ /* get the entry for the old name */
+ ret = umsdos_dentry_to_entry(olddentry, &entry);
+ if (ret)
+ goto out_unlock;
+Printk (("umsdos_link :%.*s: ino %lu flags %d ",
+entry.name_len, entry.name ,oldinode->i_ino, entry.flags));
+
+ if (!(entry.flags & UMSDOS_HIDDEN)) {
+ struct umsdos_info info;
+
+ ret = umsdos_newhidden (olddentry->d_parent, &info);
+ if (ret)
+ goto out_unlock;
+
+ ret = umsdos_rename_f (olddentry->d_inode, olddentry,
+ dir, dentry, UMSDOS_HIDDEN);
+ if (ret)
+ goto out_unlock;
+ path = d_path(olddentry, (char *) buffer, PAGE_SIZE);
+ if (!path)
+ goto out_unlock;
+ temp = umsdos_lookup_dentry(olddentry->d_parent, entry.name,
+ entry.name_len);
+ if (IS_ERR(temp))
+ goto out_unlock;
+ ret = umsdos_symlink_x (olddir, temp, path,
+ S_IFREG | 0777, UMSDOS_HLINK);
+ if (ret == 0) {
+ ret = umsdos_symlink_x (dir, dentry, path,
+ S_IFREG | 0777, UMSDOS_HLINK);
+ }
+ dput(temp);
+ goto out_unlock;
+ }
+ path = d_path(olddentry, (char *) buffer, PAGE_SIZE);
+ if (path) {
+ ret = umsdos_symlink_x (dir, dentry, path,
+ S_IFREG | 0777, UMSDOS_HLINK);
+ }
+
+out_unlock:
+ umsdos_unlockcreate (olddir);
+ umsdos_unlockcreate (dir);
+ free_page(buffer);
+out:
+ if (ret == 0) {
+ struct iattr newattrs;
+
+ oldinode->i_nlink++;
+ newattrs.ia_valid = 0;
+ ret = UMSDOS_notify_change (olddentry, &newattrs);
+ }
+ 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 successful, it
+ * is then added to the EMD file.
+ *
+ * Return the status of the operation. 0 mean success.
+ */
+int UMSDOS_create (struct inode *dir, struct dentry *dentry, int mode)
+{
+ int ret;
+ Printk ((KERN_ERR "UMSDOS_create: entering\n"));
+ check_dentry_path (dentry, "UMSDOS_create START");
+ ret = umsdos_create_any (dir, dentry, mode, 0, 0);
+ check_dentry_path (dentry, "UMSDOS_create END");
+ return ret;
+}
+
+
+/*
+ * Add a sub-directory in a directory
+ */
+/* #Specification: mkdir / Directory already exist in DOS
+ * We do the same thing as for file creation.
+ * For all user it is an error.
+ */
+/* #Specification: mkdir / umsdos directory / create EMD
+ * When we created a new sub-directory in a UMSDOS
+ * directory (one with full UMSDOS semantics), we
+ * create immediately an EMD file in the new
+ * sub-directory so it inherits UMSDOS semantics.
+ */
+int UMSDOS_mkdir (struct inode *dir, struct dentry *dentry, int mode)
+{
+ struct dentry *temp;
+ struct inode *inode;
+ int ret, err;
+ struct umsdos_info info;
+
+ ret = umsdos_nevercreat (dir, dentry, -EEXIST);
+ if (ret)
+ goto out;
+
+ ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
+ if (ret) {
+Printk (("umsdos_mkdir %d\n", ret));
+ goto out;
+ }
+
+ umsdos_lockcreate (dir);
+ info.entry.mode = mode | S_IFDIR;
+ info.entry.rdev = 0;
+ info.entry.uid = current->fsuid;
+ info.entry.gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
+ info.entry.ctime = info.entry.atime = info.entry.mtime = CURRENT_TIME;
+ info.entry.flags = 0;
+ info.entry.nlink = 1;
+ ret = umsdos_newentry (dentry->d_parent, &info);
+ if (ret) {
+Printk (("newentry %d ", ret));
+ goto out_unlock;
+ }
+
+ /* lookup the short name dentry */
+ temp = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname,
+ info.fake.len);
+ ret = PTR_ERR(temp);
+ if (IS_ERR(temp))
+ goto out_unlock;
+
+ /* Make sure the short name doesn't exist */
+ ret = -EEXIST;
+ if (temp->d_inode) {
+printk("umsdos_mkdir: short name %s/%s exists\n",
+dentry->d_parent->d_name.name, info.fake.fname);
+ goto out_remove;
+ }
+
+ ret = msdos_mkdir (dir, temp, mode);
+ if (ret)
+ goto out_remove;
+
+ inode = temp->d_inode;
+ umsdos_lookup_patch_new(temp, &info.entry, info.f_pos);
+
+ /*
+ * Note! The long and short name might be the same,
+ * so check first before doing the instantiate ...
+ */
+ if (dentry != temp) {
+ if (!dentry->d_inode) {
+ inode->i_count++;
+ d_instantiate(dentry, inode);
+ } else {
+ printk("umsdos_mkdir: not negative??\n");
+ }
+ } else {
+ printk("umsdos_mkdir: dentries match, skipping inst\n");
+ }
+
+ /* create the EMD file */
+ err = umsdos_make_emd(dentry);
+
+ /*
+ * set up the dir so it is promoted to EMD,
+ * with the EMD file invisible inside it.
+ */
+ umsdos_setup_dir(temp);
+ goto out_dput;
+
+out_remove:
+ umsdos_delentry (dentry->d_parent, &info, 1);
+
+out_dput:
+ /* kill off the short name dentry */
+ if (temp != dentry)
+ d_drop(temp);
+ dput(temp);
+
+out_unlock:
+ umsdos_unlockcreate (dir);
+ Printk (("umsdos_mkdir %d\n", ret));
+out:
+ return ret;
+}
+
+/*
+ * Add a new device special file into a directory.
+ *
+ * #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 those 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.
+ */
+int UMSDOS_mknod (struct inode *dir, struct dentry *dentry,
+ int mode, int rdev)
+{
+ int ret;
+ check_dentry_path (dentry, "UMSDOS_mknod START");
+ ret = umsdos_create_any (dir, dentry, mode, rdev, 0);
+ check_dentry_path (dentry, "UMSDOS_mknod END");
+ return ret;
+}
+
+/*
+ * Remove a sub-directory.
+ */
+int UMSDOS_rmdir (struct inode *dir, struct dentry *dentry)
+{
+ struct dentry *temp;
+ int ret, err, empty;
+ struct umsdos_info info;
+
+ ret = umsdos_nevercreat (dir, dentry, -EPERM);
+ if (ret)
+ goto out;
+
+#if 0 /* no need for lookup ... we have a dentry ... */
+ ret = umsdos_lookup_x (dir, dentry, 0);
+ Printk (("rmdir lookup %d ", ret));
+ if (ret != 0)
+ goto out;
+#endif
+
+ umsdos_lockcreate (dir);
+ ret = -EBUSY;
+ if (dentry->d_count > 1) {
+ shrink_dcache_parent(dentry);
+ if (dentry->d_count > 1) {
+printk("umsdos_rmdir: %s/%s busy\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+ goto out_unlock;
+ }
+ }
+
+ /* check whether the EMD is empty */
+ empty = umsdos_isempty (dentry);
+ ret = -ENOTEMPTY;
+ if (empty == 0)
+ goto out_unlock;
+
+ /* Have to remove the EMD file? */
+ if (empty == 1) {
+ struct dentry *demd;
+ /* check sticky bit */
+ ret = -EPERM;
+ if (is_sticky(dir, dentry->d_inode->i_uid)) {
+printk("umsdos_rmdir: %s/%s is sticky\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+ goto out_unlock;
+ }
+
+ ret = -ENOTEMPTY;
+ /* see if there's an EMD file ... */
+ demd = umsdos_get_emd_dentry(dentry);
+ if (IS_ERR(demd))
+ goto out_unlock;
+printk("umsdos_rmdir: got EMD dentry %s/%s, inode=%p\n",
+demd->d_parent->d_name.name, demd->d_name.name, demd->d_inode);
+
+ err = msdos_unlink (dentry->d_inode, demd);
+Printk (("UMSDOS_rmdir: unlinking empty EMD err=%d", err));
+ dput(demd);
+ if (err)
+ goto out_unlock;
+ }
+
+ umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
+ /* Call findentry to complete the mangling */
+ umsdos_findentry (dentry->d_parent, &info, 2);
+ temp = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname,
+ info.fake.len);
+ ret = PTR_ERR(temp);
+ if (IS_ERR(temp))
+ goto out_unlock;
+ /*
+ * If the short name matches the dentry, dput() it now.
+ */
+ if (temp == dentry) {
+ dput(temp);
+printk("umsdos_rmdir: %s/%s, short matches long\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+ }
+
+ /*
+ * Attempt to remove the msdos name.
+ */
+ ret = msdos_rmdir (dir, temp);
+ if (ret && ret != -ENOENT)
+ goto out_dput;
+
+ /* OK so far ... remove the name from the EMD */
+ ret = umsdos_delentry (dentry->d_parent, &info, 1);
+
+out_dput:
+ /* dput() temp if we didn't do it above */
+ if (temp != dentry) {
+ d_drop(temp);
+ dput(temp);
+ if (!ret)
+ d_delete (dentry);
+printk("umsdos_rmdir: %s/%s, short=%s dput\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, info.fake.fname);
+ }
+
+out_unlock:
+ umsdos_unlockcreate (dir);
+
+out:
+ Printk (("umsdos_rmdir %d\n", ret));
+ return ret;
+}
+
+
+/*
+ * Remove a file from the directory.
+ *
+ * #Specification: hard link / deleting a link
+ * When we delete a file, and this file is a link
+ * we must subtract 1 to the nlink field of the
+ * hidden link.
+ *
+ * If the count goes to 0, we delete this hidden
+ * link too.
+ */
+int UMSDOS_unlink (struct inode *dir, struct dentry *dentry)
+{
+ struct dentry *temp;
+ struct inode *inode;
+ int ret;
+ struct umsdos_info info;
+
+Printk (("UMSDOS_unlink: entering %s/%s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name));
+
+ ret = umsdos_nevercreat (dir, dentry, -EPERM);
+ if (ret)
+ goto out;
+
+ ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
+ if (ret)
+ goto out;
+
+ umsdos_lockcreate (dir);
+ ret = umsdos_findentry (dentry->d_parent, &info, 1);
+ if (ret) {
+printk("UMSDOS_unlink: findentry returned %d\n", ret);
+ goto out_unlock;
+ }
+
+Printk (("UMSDOS_unlink %.*s ", info.fake.len, info.fake.fname));
+ ret = -EPERM;
+ /* check sticky bit */
+ if (is_sticky(dir, info.entry.uid)) {
+printk("umsdos_unlink: %s/%s is sticky\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+ goto out_unlock;
+ }
+
+ ret = 0;
+ if (info.entry.flags & UMSDOS_HLINK) {
+printk("UMSDOS_unlink: hard link %s/%s, fake=%s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, info.fake.fname);
+ /*
+ * First, get the inode of the hidden link
+ * using the standard lookup function.
+ */
+
+ ret = umsdos_lookup_x (dir, dentry, 0);
+ inode = dentry->d_inode;
+ if (ret)
+ goto out_unlock;
+
+ Printk (("unlink nlink = %d ", inode->i_nlink));
+ inode->i_nlink--;
+ if (inode->i_nlink == 0) {
+ struct umsdos_dirent entry;
+
+ ret = umsdos_dentry_to_entry (dentry, &entry);
+ if (ret == 0) {
+ ret = UMSDOS_unlink (dentry->d_parent->d_inode,
+ dentry);
+ }
+ } else {
+ struct iattr newattrs;
+ newattrs.ia_valid = 0;
+ ret = UMSDOS_notify_change (dentry, &newattrs);
+ }
+ }
+ if (ret)
+ goto out_unlock;
+
+ /* get the short name dentry */
+ temp = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname,
+ info.fake.len);
+ if (IS_ERR(temp))
+ goto out_unlock;
+
+ /*
+ * If the short name matches the long,
+ * dput() it now so it's not busy.
+ */
+ if (temp == dentry) {
+printk("UMSDOS_unlink: %s/%s, short matches long\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+ dput(temp);
+ }
+
+ ret = umsdos_delentry (dentry->d_parent, &info, 0);
+ if (ret && ret != -ENOENT)
+ goto out_dput;
+
+printk("UMSDOS: Before msdos_unlink %.*s ",
+info.fake.len, info.fake.fname);
+ ret = msdos_unlink_umsdos (dir, temp);
+
+Printk (("msdos_unlink %.*s %o ret %d ",
+info.fake.len, info.fake.fname ,info.entry.mode, ret));
+
+ /* dput() temp if we didn't do it above */
+out_dput:
+ if (temp != dentry) {
+ d_drop(temp);
+ dput(temp);
+ if (!ret)
+ d_delete (dentry);
+printk("umsdos_unlink: %s/%s, short=%s dput\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, info.fake.fname);
+ }
+
+out_unlock:
+ umsdos_unlockcreate (dir);
+out:
+ Printk (("umsdos_unlink %d\n", ret));
+ return ret;
+}
+
+
+/*
+ * Rename (move) a file.
+ */
+int UMSDOS_rename (struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ int ret;
+
+ ret = umsdos_nevercreat (new_dir, new_dentry, -EEXIST);
+ if (ret)
+ goto out;
+
+ ret = umsdos_rename_f(old_dir, old_dentry, new_dir, new_dentry, 0);
+ if (ret != -EEXIST)
+ goto out;
+
+ /* This is not terribly efficient but should work. */
+ ret = UMSDOS_unlink (new_dir, new_dentry);
+ chkstk ();
+ Printk (("rename unlink ret %d -- ", ret));
+ if (ret == -EISDIR) {
+ ret = UMSDOS_rmdir (new_dir, new_dentry);
+ chkstk ();
+ Printk (("rename rmdir ret %d -- ", ret));
+ }
+ if (ret)
+ goto out;
+
+ /* this time the rename should work ... */
+ ret = umsdos_rename_f (old_dir, old_dentry, new_dir, new_dentry, 0);
+
+out:
+ return ret;
+}
#include <asm/uaccess.h>
-#define PRINTK(x)
-#define Printk(x) printk x
-
extern struct inode *pseudo_root;
@@ -39,16+36,13 @@ static int rdir_filldir ( void *buf, int ret = 0;
struct RDIR_FILLDIR *d = (struct RDIR_FILLDIR *) buf;
- PRINTK ((KERN_DEBUG "rdir_filldir /mn/: entering\n"));
if (d->real_root) {
PRINTK ((KERN_DEBUG "rdir_filldir /mn/: real root!\n"));
/* real root of a pseudo_rooted partition */
if (name_len != UMSDOS_PSDROOT_LEN
|| memcmp (name, UMSDOS_PSDROOT_NAME, UMSDOS_PSDROOT_LEN) != 0) {
/* So it is not the /linux directory */
- if (name_len == 2
- && name[0] == '.'
- && name[1] == '.') {
+ if (name_len == 2 && name[0] == '.' && name[1] == '.') {
/* Make sure the .. entry points back to the pseudo_root */
ino = pseudo_root->i_ino;
}
@@ -56,30+50,22 @@ static int rdir_filldir ( void *buf, }
} else {
/* Any DOS directory */
- PRINTK ((KERN_DEBUG "rdir_filldir /mn/: calling d->filldir (%p) for %.*s (%lu)\n", d->filldir, name_len, name, ino));
ret = d->filldir (d->dirbuf, name, name_len, offset, ino);
}
return ret;
}
-static int UMSDOS_rreaddir (
- struct file *filp,
- void *dirbuf,
- filldir_t filldir)
+static int UMSDOS_rreaddir (struct file *filp, void *dirbuf, filldir_t filldir)
{
- struct RDIR_FILLDIR bufk;
struct inode *dir = filp->f_dentry->d_inode;
-
- PRINTK ((KERN_DEBUG "UMSDOS_rreaddir /mn/: entering %p %p\n", filldir, dirbuf));
-
+ struct RDIR_FILLDIR bufk;
bufk.filldir = filldir;
bufk.dirbuf = dirbuf;
- bufk.real_root = pseudo_root
- && dir == iget (dir->i_sb, UMSDOS_ROOT_INO)
- && dir == iget (pseudo_root->i_sb, UMSDOS_ROOT_INO);
- PRINTK ((KERN_DEBUG "UMSDOS_rreaddir /mn/: calling fat_readdir with filldir=%p and exiting\n", filldir));
+ bufk.real_root = pseudo_root &&
+ dir->i_ino == UMSDOS_ROOT_INO &&
+ dir->i_sb == pseudo_root->i_sb;
return fat_readdir (filp, &bufk, rdir_filldir);
}
@@ -89,157+75,138 @@ static int UMSDOS_rreaddir ( * If the result is a directory, make sure we find out if it is
* a promoted one or not (calling umsdos_setup_dir_inode(inode)).
*/
-int umsdos_rlookup_x (
- struct inode *dir,
- struct dentry *dentry,
- int nopseudo)
-{ /* Don't care about pseudo root mode */
+/* #Specification: pseudo root / DOS/..
+ * In the real root directory (c:\), the directory ..
+ * is the pseudo root (c:\linux).
+ */
+int umsdos_rlookup_x ( struct inode *dir, struct dentry *dentry, int nopseudo)
+{
/* so locating "linux" will work */
- int len = dentry->d_name.len;
const char *name = dentry->d_name.name;
+ int len = dentry->d_name.len;
struct inode *inode;
int ret;
- if (pseudo_root
- && len == 2
- && name[0] == '.'
- && name[1] == '.'
- && dir == iget (dir->i_sb, UMSDOS_ROOT_INO)
- && dir == iget (pseudo_root->i_sb, UMSDOS_ROOT_INO)) {
- /* *result = pseudo_root; */
- Printk ((KERN_WARNING "umsdos_rlookup_x: we are at pseudo-root thingy?\n"));
- inc_count (pseudo_root);
+ if (pseudo_root && len == 2 && name[0] == '.' && name[1] == '.' &&
+ dir->i_ino == UMSDOS_ROOT_INO && dir->i_sb == pseudo_root->i_sb) {
+printk (KERN_WARNING "umsdos_rlookup_x: we are at pseudo-root thingy?\n");
+ pseudo_root->i_count++;
+ d_add(dentry, pseudo_root);
ret = 0;
- /* #Specification: pseudo root / DOS/..
- * In the real root directory (c:\), the directory ..
- * is the pseudo root (c:\linux).
- */
- } else {
- inc_count (dir);
- ret = umsdos_real_lookup (dir, dentry);
- inode = dentry->d_inode;
+ goto out;
+ }
-#if 0
- Printk ((KERN_DEBUG "umsdos_rlookup_x: umsdos_real_lookup for %.*s in %lu returned %d\n", len, name, dir->i_ino, ret));
- Printk ((KERN_DEBUG "umsdos_rlookup_x: umsdos_real_lookup: inode is %p resolving to ", inode));
- if (inode) { /* /mn/ FIXME: DEL_ME */
- Printk ((KERN_DEBUG "i_ino=%lu\n", inode->i_ino));
- } else {
- Printk ((KERN_DEBUG "NONE!\n"));
+ ret = umsdos_real_lookup (dir, dentry);
+ inode = dentry->d_inode;
+ if ((ret == 0) && inode) {
+ if (inode == pseudo_root && !nopseudo) {
+ /* #Specification: pseudo root / DOS/linux
+ * Even in the real root directory (c:\), the directory
+ * /linux won't show
+ */
+printk(KERN_WARNING "umsdos_rlookup_x: do the pseudo-thingy...\n");
+ /* make the dentry negative */
+ d_delete(dentry);
}
-#endif
-
- if ((ret == 0) && inode) {
-
- if (pseudo_root && inode == pseudo_root && !nopseudo) {
- /* #Specification: pseudo root / DOS/linux
- * Even in the real root directory (c:\), the directory
- * /linux won't show
- */
- Printk ((KERN_WARNING "umsdos_rlookup_x: do the pseudo-thingy...\n"));
- ret = -ENOENT;
- iput (pseudo_root); /* FIXME? */
-
- } else if (S_ISDIR (inode->i_mode)) {
- /* We must place the proper function table */
- /* depending on whether this is an MS-DOS or a UMSDOS directory */
- Printk ((KERN_DEBUG "umsdos_rlookup_x: setting up setup_dir_inode %lu...\n", inode->i_ino));
- umsdos_setup_dir_inode (inode);
- }
+ else if (S_ISDIR (inode->i_mode)) {
+ /* We must place the proper function table
+ * depending on whether this is an MS-DOS or
+ * a UMSDOS directory
+ */
+Printk ((KERN_DEBUG "umsdos_rlookup_x: setting up setup_dir_inode %lu...\n",
+inode->i_ino));
+ umsdos_setup_dir(dentry);
}
- iput (dir);
}
+out:
PRINTK ((KERN_DEBUG "umsdos_rlookup_x: returning %d\n", ret));
return ret;
}
-int UMSDOS_rlookup (
- struct inode *dir,
- struct dentry *dentry
-)
+int UMSDOS_rlookup ( struct inode *dir, struct dentry *dentry)
{
- PRINTK ((KERN_DEBUG "UMSDOS_rlookup /mn/: executing umsdos_rlookup_x for ino=%lu in %.*s\n", dir->i_ino, (int) dentry->d_name.len, dentry->d_name.name));
return umsdos_rlookup_x (dir, dentry, 0);
}
-static int UMSDOS_rrmdir (
- struct inode *dir,
- struct dentry *dentry)
+/* #Specification: dual mode / rmdir in a DOS directory
+ * In a DOS (not EMD in it) directory, we use a reverse strategy
+ * compared with a 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 makes 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 subdirectories are also
+ * Umsdos directories, so we check the EMD file first.
+ */
+/* #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
+ * MS-DOS) is always possible.
+ *
+ * EPERM is returned.
+ */
+static int UMSDOS_rrmdir ( struct inode *dir, struct dentry *dentry)
{
- /* #Specification: dual mode / rmdir in a DOS directory
- * In a DOS (not EMD in it) directory, we use a reverse strategy
- * compared with a 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;
+ int ret, empty;
- if (umsdos_is_pseudodos (dir, dentry)) {
- /* #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
- * MS-DOS) is always possible.
- *
- * EPERM is returned.
- */
- ret = -EPERM;
- } else {
- umsdos_lockcreate (dir);
- inc_count (dir);
- ret = msdos_rmdir (dir, dentry);
- if (ret == -ENOTEMPTY) {
- struct inode *sdir;
+ ret = -EPERM;
+ if (umsdos_is_pseudodos (dir, dentry))
+ goto out;
- inc_count (dir);
+ umsdos_lockcreate (dir);
+ ret = -EBUSY;
+ if (dentry->d_count > 1) {
+ shrink_dcache_parent(dentry);
+ if (dentry->d_count > 1)
+ goto out_unlock;
+ }
- ret = UMSDOS_rlookup (dir, dentry);
- sdir = dentry->d_inode;
- PRINTK (("rrmdir lookup %d ", ret));
- if (ret == 0) {
- int empty;
+ ret = msdos_rmdir (dir, dentry);
+ if (ret != -ENOTEMPTY)
+ goto out_check;
- if ((empty = umsdos_isempty (sdir)) != 0) {
- PRINTK (("isempty %d i_count %d ", empty,
- atomic_read (&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 remove the EMD file. */
- struct dentry *temp;
+#if 0 /* why do this? we have the dentry ... */
+ ret = UMSDOS_rlookup (dir, dentry);
+ PRINTK (("rrmdir lookup %d ", ret));
+ if (ret)
+ goto out_unlock;
+ ret = -ENOTEMPTY;
+#endif
- Printk ((KERN_WARNING "UMSDOS_rmdir: hmmm... what about inode? FIXME\n"));
- temp = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL, NULL); /* FIXME: probably should fill inode part ? */
- ret = msdos_unlink (sdir, temp);
- sdir = NULL;
- if (ret == 0) {
- inc_count (dir);
- ret = msdos_rmdir (dir, dentry);
- }
- }
- } else {
- ret = -ENOTEMPTY;
- }
- /* iput (sdir); FIXME */
- }
+ empty = umsdos_isempty (dentry);
+ if (empty == 1) {
+ struct dentry *temp;
+ /* We have to remove the EMD file. */
+ temp = umsdos_lookup_dentry(dentry, UMSDOS_EMD_FILE,
+ UMSDOS_EMD_NAMELEN);
+ ret = PTR_ERR(temp);
+ if (!IS_ERR(temp)) {
+ ret = 0;
+ if (temp->d_inode)
+ ret = msdos_unlink (dentry->d_inode, temp);
+ dput(temp);
}
- umsdos_unlockcreate (dir);
+ if (ret)
+ goto out_unlock;
}
- /* iput (dir); FIXME */
+ /* now retry the original ... */
+ ret = msdos_rmdir (dir, dentry);
+
+out_check:
+ /* Check whether we succeeded ... */
+ if (!ret)
+ d_delete(dentry);
+
+out_unlock:
+ umsdos_unlockcreate (dir);
+out:
check_inode (dir);
return ret;
}
--- /dev/null
+/* #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.
+ */
+ /* #Specification: readdir / . and ..
+ * The msdos filesystem manages 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.
+ *
+ * Now (linux 1.3), umsdos_readdir can read more than one
+ * entry even if we limit (umsdos_dir_once) to only one:
+ * It skips over hidden file. So we switch to
+ * UMSDOS_SPECIAL_DIRFPOS as soon as we have read successfully
+ * the .. entry.
+ */
+ /* #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 MS-DOS for mtime for directories
+ * because the MS-DOS date on a directory is its
+ * creation time (strange MSDOS behavior) which
+ * corresponds to none of the three Unix time stamps.
+ */
+ /* #Specification: umsdos / conversion mode
+ * The msdos filesystem can do some inline conversion
+ * of the data of a file. It can translate silently
+ * from the MS-DOS text file format to the Unix one
+ * (CRLF -> LF) while reading, and the reverse
+ * while writing. This is activated using the mount
+ * option conv=....
+ *
+ * This is not useful for Linux files in a promoted
+ * directory. It can even be harmful. For this
+ * reason, the binary (no conversion) mode is
+ * always activated.
+ */
+ /* #Specification: umsdos / conversion mode / todo
+ * A flag could be added to file and directories
+ * forcing an automatic conversion mode (as
+ * done with the msdos filesystem).
+ *
+ * This flag could be setup on a directory basis
+ * (instead of file) and all files in it would
+ * logically inherit it. If the conversion mode
+ * is active (conv=) then the i_binary flag would
+ * be left untouched in those directories.
+ *
+ * It was proposed that the sticky bit be used to set
+ * this. A problem with that is that new files would
+ * be written incorrectly. The other problem is that
+ * the sticky bit has a meaning for directories. So
+ * another bit should be used (there is some space
+ * in the EMD file for it) and a special utility
+ * would be used to assign the flag to a directory).
+ * I don't think it is useful to assign this flag
+ * on a single file.
+ */
+ * #Specification: weakness / rename
+ * There is a case where UMSDOS rename has a different behavior
+ * than a 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 may is in Linux VFS driver for msdos.
+ * I believe this is not a bug but a design feature, because
+ * an inode number represents some sort of directory address
+ * in the MSDOS directory structure, so moving the file into
+ * another directory does not preserve the inode number.
+ */
+/* #Specification: rename / new name exist
+ * If the destination name already exists, 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 ensuring that only
+ * one process could achieve a rename (modification) operation
+ * in the source and destination directory. This was also
+ * ensuring the operation was "atomic".
+ *
+ * This has been changed because this was creating a
+ * stack overflow (the stack is only 4 kB) 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 may 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.
+ */
+/* #Specification: hard link / strategy
+ * Hard links are difficult to implement on top of an MS-DOS FAT file
+ * system. Unlike Unix file systems, there are no inodes. A directory
+ * entry holds the functionality of the inode and the entry.
+ *
+ * We will used the same strategy as a normal Unix file system
+ * (with inodes) except we will do it symbolically (using paths).
+ *
+ * Because anything can happen during a DOS session (defragment,
+ * directory sorting, etc.), we can't rely on an MS-DOS pseudo
+ * inode number to record the link. For this reason, the link
+ * will be done using hidden symbolic links. The following
+ * scenario illustrates how it works.
+ *
+ * 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 or /tmp/file2 independently.
+ *
+ * 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).
+ */
+/* #Specification: hard link / directory
+ * A hard link can't be made on a directory. EPERM is returned
+ * in this case.
+ */
+/* #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
+ * symbolic 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.
+ */
+/* #Specification: function name / convention
+ * A simple convention for function names has been used in
+ * the UMSDOS filesystem. First, all functions use the prefix
+ * umsdos_ to avoid name clashes with other parts of the kernel.
+ *
+ * Standard VFS entry points use the prefix UMSDOS (upper case)
+ * so it's easier to tell them apart.
+ * N.B. (FIXME) PTW, the order and contents of this struct changed.
+ */
+
+/* #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 sense 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.
+ */
+
+/* #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 or
+ * /linux/sbin/init. 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.
+ */
@@ -100,22+100,6 @@ static struct dentry_operations vfat_dentry_ops[4] = { }
};
-static int strnicmp(const char *s1, const char *s2, int len)
-{
- int n = 0;
- while (*s1 && *s2 && (tolower(*s1) == tolower(*s2))) {
- s1++; s2++; n++;
- if (n == len) return 0;
- }
- if (*s1 == 0 && *s2 == 0) return 0;
- if (*s1 && *s2) {
- if (*s1 > *s2) return 1;
- return -1;
- }
- if (*s1) return 1;
- return -1;
-}
-
void vfat_put_super(struct super_block *sb)
{
fat_put_super(sb);
@@ -240,6+240,7 @@ static inline void flush_tlb_range(struct mm_struct *mm, #define PAGE_COPY __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED)
#define PAGE_READONLY __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED)
#define PAGE_KERNEL __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED)
+#define PAGE_KERNEL_RO __pgprot(_PAGE_PRESENT | _PAGE_DIRTY | _PAGE_ACCESSED)
/*
* The i386 can't do page protection for execute, and considers that the same are read.
#include <linux/kernel.h>
#include <asm/segment.h>
-/*
- * Entry into gdt where to find first TSS. GDT layout:
- * 0 - null
- * 1 - not used
- * 2 - kernel code segment
- * 3 - kernel data segment
- * 4 - user code segment
- * 5 - user data segment
- * 6 - not used
- * 7 - not used
- * 8 - APM BIOS support
- * 9 - APM BIOS support
- * 10 - APM BIOS support
- * 11 - APM BIOS support
- * 12 - TSS #0
- * 13 - LDT #0
- * 14 - TSS #1
- * 15 - LDT #1
- */
-#define FIRST_TSS_ENTRY 12
-#define FIRST_LDT_ENTRY (FIRST_TSS_ENTRY+1)
-#define _TSS(n) ((((unsigned long) n)<<4)+(FIRST_TSS_ENTRY<<3))
-#define _LDT(n) ((((unsigned long) n)<<4)+(FIRST_LDT_ENTRY<<3))
-#define load_TR(n) __asm__ __volatile__("ltr %%ax": /* no output */ :"a" (_TSS(n)))
-#define load_ldt(n) __asm__ __volatile__("lldt %%ax": /* no output */ :"a" (_LDT(n)))
-#define store_TR(n) \
-__asm__("str %%ax\n\t" \
- "subl %2,%%eax\n\t" \
- "shrl $4,%%eax" \
- :"=a" (n) \
- :"0" (0),"i" (FIRST_TSS_ENTRY<<3))
-
#ifdef __KERNEL__
struct task_struct; /* one of the stranger aspects of C forward declarations.. */
@@ -227,16+195,6 @@ extern void __global_restore_flags(unsigned long);
#endif
-extern void set_intr_gate(unsigned int irq, void * addr);
-void set_ldt_desc(void *n, void *addr, unsigned int size);
-void set_tss_desc(void *n, void *addr);
-
-/*
- * This is the ldt that every process will get unless we need
- * something other than this.
- */
-extern struct desc_struct default_ldt;
-
/*
* disable hlt during certain critical i/o operations
*/
+++ /dev/null
-#ifndef _LINUX_HEAD_H
-#define _LINUX_HEAD_H
-
-struct desc_struct {
- unsigned long a,b;
-};
-
-extern struct desc_struct idt_table[],gdt_table[];
-extern struct desc_struct *idt, *gdt;
-
-struct Xgt_desc_struct {
- unsigned short size;
- unsigned long address __attribute__((packed));
-};
-
-#define idt_descr (*(struct Xgt_desc_struct *)((char *)&idt - 2))
-#define gdt_descr (*(struct Xgt_desc_struct *)((char *)&gdt - 2))
-
-#define GDT_NUL 0
-#define GDT_CODE 1
-#define GDT_DATA 2
-#define GDT_TMP 3
-
-#define LDT_NUL 0
-#define LDT_CODE 1
-#define LDT_DATA 2
-
-#endif
@@ -64,7+64,6 @@ extern unsigned long avenrun[]; /* Load averages */ extern int nr_running, nr_tasks;
extern int last_pid;
-#include <linux/head.h>
#include <linux/fs.h>
#include <linux/signal.h>
#include <linux/time.h>
#ifndef LINUX_UMSDOS_FS_H
#define LINUX_UMSDOS_FS_H
-#define UMS_DEBUG 1
+/* #define UMSDOS_DEBUG 1 */
+#define UMSDOS_PARANOIA 1
#define UMSDOS_VERSION 0
#define UMSDOS_RELEASE 4
#ifndef _LINUX_TYPES_H
#include <linux/types.h>
#endif
+#ifndef _LINUX_LIMITS_H
+#include <linux/limits.h>
+#endif
#ifndef _LINUX_DIRENT_H
#include <linux/dirent.h>
#endif
* to shut off.
*/
# define PRINTK(x)
-# ifdef UMS_DEBUG
+# ifdef UMSDOS_DEBUG
# define Printk(x) printk x
# else
# define Printk(x)
/* check.c 23/01/95 03.38.30 */
void check_page_tables (void);
+
/* dir.c 22/06/95 00.22.12 */
int compat_msdos_create(struct inode *dir,
const char *name,
@@ -10,10+11,12 @@ int dummy_dir_read ( struct file *filp, char *buf,
size_t size,
loff_t *count);
+void umsdos_lookup_patch_new(struct dentry *, struct umsdos_dirent *, off_t);
void umsdos_lookup_patch (struct inode *dir,
struct inode *inode,
struct umsdos_dirent *entry,
off_t emd_pos);
+int umsdos_dentry_to_entry (struct dentry *, struct umsdos_dirent *);
int umsdos_inode2entry (struct inode *dir,
struct inode *inode,
struct umsdos_dirent *entry);
@@ -23,16+26,16 @@ int umsdos_lookup_x ( struct inode *dir,
struct dentry *dentry,
int nopseudo);
-int UMSDOS_lookup(struct inode *dir,struct dentry *dentry);
+int UMSDOS_lookup(struct inode *, struct dentry *);
+struct dentry *umsdos_lookup_dentry(struct dentry *, char *, int);
struct dentry *umsdos_solve_hlink (struct dentry *hlink);
+
/* emd.c 22/06/95 00.22.04 */
ssize_t umsdos_file_write_kmem_real (struct file *filp,
const char *buf,
size_t count);
-int fix_emd_filp (struct file *filp);
-
ssize_t umsdos_file_read_kmem (struct file *filp,
char *buf,
size_t count);
@@ -45,61+48,52 @@ ssize_t umsdos_emd_dir_write (struct file *filp, ssize_t umsdos_emd_dir_read (struct file *filp,
char *buf,
size_t count);
+struct dentry *umsdos_get_emd_dentry(struct dentry *);
+int umsdos_have_emd(struct dentry *);
+int umsdos_make_emd(struct dentry *);
struct inode *umsdos_emd_dir_lookup (struct inode *dir, int creat);
-int umsdos_emd_dir_readentry (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);
+int umsdos_emd_dir_readentry (struct file *, struct umsdos_dirent *);
+int umsdos_newentry (struct dentry *, struct umsdos_info *);
+int umsdos_newhidden (struct dentry *, struct umsdos_info *);
+int umsdos_delentry (struct dentry *, struct umsdos_info *, int);
+int umsdos_findentry (struct dentry *, struct umsdos_info *, int);
+int umsdos_isempty (struct dentry *);
+
/* file.c 25/01/95 02.25.38 */
+
/* inode.c 12/06/95 09.49.40 */
inline struct dentry *geti_dentry (struct inode *inode);
void checkd_inode (struct inode *inode);
-inline void inc_count (struct inode *inode);
void check_inode (struct inode *inode);
void check_dentry (struct dentry *dentry);
void check_dentry_path (struct dentry *dentry, const char *desc);
void fill_new_filp (struct file *filp, struct dentry *dentry);
-void kill_dentry (struct dentry *dentry);
-void fin_dentry (struct dentry *dentry);
struct dentry *creat_dentry (const char *name,
const int len,
struct inode *inode,
struct dentry *parent);
-void UMSDOS_put_inode (struct inode *inode);
-void UMSDOS_put_super (struct super_block *sb);
-int UMSDOS_statfs (struct super_block *sb,
- struct statfs *buf,
- int bufsiz);
+void UMSDOS_read_inode (struct inode *);
+void UMSDOS_write_inode (struct inode *);
+int UMSDOS_notify_change (struct dentry *, struct iattr *attr);
+void UMSDOS_put_inode (struct inode *);
+int UMSDOS_statfs (struct super_block *, struct statfs *, int);
+struct super_block *UMSDOS_read_super (struct super_block *, void *, int);
+void UMSDOS_put_super (struct super_block *);
+
struct dentry *compat_umsdos_real_lookup (struct dentry *d_dir,
const char *name,
int len);
-int umsdos_real_lookup(struct inode *inode,struct dentry *dentry);
+int umsdos_real_lookup(struct inode *, struct dentry *);
+void umsdos_setup_dir(struct dentry *);
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);
+void umsdos_patch_dentry_inode (struct dentry *, off_t);
+void umsdos_patch_inode (struct inode *, struct inode *, off_t);
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 (struct dentry *dentry, struct iattr *attr);
-struct super_block *UMSDOS_read_super (struct super_block *s,
- void *data,
- int silent);
+
/* ioctl.c 22/06/95 00.22.08 */
int UMSDOS_ioctl_dir (struct inode *dir,
struct file *filp,
@@ -109,6+103,7 @@ int UMSDOS_ioctl_dir (struct inode *dir, void umsdos_manglename (struct umsdos_info *info);
int umsdos_evalrecsize (int len);
int umsdos_parse (const char *name,int len, struct umsdos_info *info);
+
/* namei.c 25/01/95 02.25.38 */
void umsdos_lockcreate (struct inode *dir);
void umsdos_startlookup (struct inode *dir);
@@ -141,6+136,7 @@ int UMSDOS_rename (struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir,
struct dentry *new_dentry);
+
/* rdir.c 22/03/95 03.31.42 */
int umsdos_rlookup_x (struct inode *dir,
struct dentry *dentry,
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/tty.h>
-#include <linux/head.h>
#include <linux/unistd.h>
#include <linux/string.h>
#include <linux/timer.h>
@@ -1109,9+1108,8 @@ asmlinkage void __init start_kernel(void) #if defined(CONFIG_QUOTA)
dquot_init_hash();
#endif
- printk("POSIX conformance testing by UNIFIX\n");
-
check_bugs();
+ printk("POSIX conformance testing by UNIFIX\n");
smp_init();
#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/config.h>
#include <linux/mm.h>
#include <linux/sched.h>
-#include <linux/head.h>
#include <linux/kernel.h>
#include <linux/kernel_stat.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
-#include <linux/head.h>
#include <linux/kernel.h>
#include <linux/kernel_stat.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
-#include <linux/head.h>
#include <linux/kernel.h>
#include <linux/kernel_stat.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
-#include <linux/head.h>
#include <linux/kernel.h>
#include <linux/kernel_stat.h>
#include <linux/errno.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/sched.h>
-#include <linux/head.h>
#include <linux/kernel.h>
#include <linux/kernel_stat.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
-#include <linux/head.h>
#include <linux/kernel.h>
#include <linux/kernel_stat.h>
#include <linux/errno.h>
@@ -292,7+292,7 @@ static void tcp_sacktag_write_queue(struct sock *sk, struct tcp_sack_block *sp, /* The retransmission queue is always in order, so
* we can short-circuit the walk early.
*/
- if(!before(start_seq, TCP_SKB_CB(skb)->end_seq))
+ if(after(TCP_SKB_CB(skb)->end_seq, end_seq))
break;
/* We play conservative, we don't allow SACKS to partially
@@ -471,10+471,9 @@ static void tcp_fast_retrans(struct sock *sk, u32 ack, int not_dup) * to one half the current congestion window, but no less
* than two segments. Retransmit the missing segment.
*/
+ tp->dup_acks++;
if (tp->high_seq == 0 || after(ack, tp->high_seq)) {
- tp->dup_acks++;
if ((tp->fackets_out > 3) || (tp->dup_acks == 3)) {
- tp->dup_acks++;
tp->snd_ssthresh = max(tp->snd_cwnd >> (TCP_CWND_SHIFT + 1), 2);
tp->snd_cwnd = (tp->snd_ssthresh + 3) << TCP_CWND_SHIFT;
tp->high_seq = tp->snd_nxt;
@@ -1712,6+1711,10 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb, */
if (flg == tp->pred_flags && TCP_SKB_CB(skb)->seq == tp->rcv_nxt) {
+ if (!tcp_sequence(tp, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq)) {
+ tcp_send_ack(sk);
+ goto discard;
+ }
if (len <= th->doff*4) {
/* Bulk data transfer: sender */
if (len == th->doff*4) {
@@ -1726,13+1729,8 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb, }
} else if (TCP_SKB_CB(skb)->ack_seq == tp->snd_una) {
/* Bulk data transfer: receiver */
- if (atomic_read(&sk->rmem_alloc) > sk->rcvbuf) {
- /* We must send an ACK for zero window probes. */
- if (!before(TCP_SKB_CB(skb)->seq,
- tp->rcv_wup + tp->rcv_wnd))
- tcp_send_ack(sk);
+ if (atomic_read(&sk->rmem_alloc) > sk->rcvbuf)
goto discard;
- }
__skb_pull(skb,th->doff*4);