diff -u --recursive --new-file v2.1.31/linux/CREDITS linux/CREDITS --- v2.1.31/linux/CREDITS Thu Mar 27 14:39:58 1997 +++ linux/CREDITS Sat Apr 5 12:14:20 1997 @@ -632,6 +632,13 @@ S: D-69126 Heidelberg S: Germany +N: Christopher Horn +E: chorn@warwick.net +D: Miscellaneous sysctl hacks +S: 36 Mudtown Road +S: Wantage, NJ 07461 +S: USA + N: Miguel de Icaza Amozurrutia E: miguel@nuclecu.unam.mx D: Linux/SPARC team, Midnight Commander maintainer diff -u --recursive --new-file v2.1.31/linux/Documentation/Changes linux/Documentation/Changes --- v2.1.31/linux/Documentation/Changes Fri Apr 4 08:52:16 1997 +++ linux/Documentation/Changes Fri Apr 4 09:34:31 1997 @@ -47,6 +47,7 @@ - Kbd 0.91 - Loadlin 1.6a - Sh-utils 1.16 +- Ncpfs 2.1.1 Upgrade notes ************* diff -u --recursive --new-file v2.1.31/linux/Documentation/Configure.help linux/Documentation/Configure.help --- v2.1.31/linux/Documentation/Configure.help Fri Apr 4 08:52:16 1997 +++ linux/Documentation/Configure.help Sat Apr 5 12:14:20 1997 @@ -703,6 +703,18 @@ or with the program info ("man info"). Saying Y here enlarges your kernel by about 7kB. Just say Y. +Sysctl support +CONFIG_SYSCTL + The sysctl interface provides a means of dynamically changing certain + kernel parameters and variables on the fly without requiring a + recompile of the kernel or reboot of the system. The primary interface + consists of a system call, but if the /proc filesystem is enabled, a + tree of modifiable sysctl entries will be generated beneath the + /proc/sys directory. Note that enabling this option will enlarge the + kernel by at least 8kB. As it is generally a good thing, you probably + want to say Y here unless building a kernel for install/rescue disks + or your system is very limited in memory. + Kernel support for ELF binaries CONFIG_BINFMT_ELF ELF (Executable and Linkable Format) is a format for libraries and diff -u --recursive --new-file v2.1.31/linux/Documentation/ioctl-number.txt linux/Documentation/ioctl-number.txt --- v2.1.31/linux/Documentation/ioctl-number.txt Sun Feb 2 05:18:29 1997 +++ linux/Documentation/ioctl-number.txt Thu Apr 3 15:00:40 1997 @@ -1,5 +1,5 @@ Ioctl Numbers -16 Dec 1996 +12 Feb 1997 Michael Chastain @@ -108,3 +108,5 @@ 0x90 00 linux/sbpcd.h 0x99 00-0F 537-Addinboard driver in development, e-mail contact: b.kohl@ipn-b.comlink.apc.org +0xA0 all Small Device Project in development, e-mail contact: + khollis@northwest.com diff -u --recursive --new-file v2.1.31/linux/MAINTAINERS linux/MAINTAINERS --- v2.1.31/linux/MAINTAINERS Fri Apr 4 08:52:16 1997 +++ linux/MAINTAINERS Fri Apr 4 09:34:31 1997 @@ -249,7 +249,7 @@ NCP FILESYSTEM: P: Volker Lendecke -M: lendecke@namu01.Num.Math.Uni-Goettingen.de +M: lendecke@Math.Uni-Goettingen.de L: linware@sh.cvut.cz S: Maintained @@ -301,9 +301,9 @@ SMB FILESYSTEM: P: Volker Lendecke -M: lendecke@namu01.Num.Math.Uni-Goettingen.de +M: lendecke@Math.Uni-Goettingen.de L: samba@listproc.anu.edu.au -S: Odd Fixes +S: Maintained SMP: (except SPARC) P: Alan Cox diff -u --recursive --new-file v2.1.31/linux/Makefile linux/Makefile --- v2.1.31/linux/Makefile Fri Apr 4 08:52:16 1997 +++ linux/Makefile Fri Apr 4 11:56:59 1997 @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 1 -SUBLEVEL = 31 +SUBLEVEL = 32 ARCH = i386 @@ -114,8 +114,9 @@ # Include the make variables (CC, etc...) # -ARCHIVES =kernel/kernel.o mm/mm.o fs/fs.o ipc/ipc.o net/network.a +CORE_FILES =kernel/kernel.o mm/mm.o fs/fs.o ipc/ipc.o FILESYSTEMS =fs/filesystems.a +NETWORKS =net/network.a DRIVERS =drivers/block/block.a \ drivers/char/char.a LIBS =$(TOPDIR)/lib/lib.a @@ -173,8 +174,9 @@ vmlinux: $(CONFIGURATION) init/main.o init/version.o linuxsubdirs $(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o \ - $(ARCHIVES) \ + $(CORE_FILES) \ $(FILESYSTEMS) \ + $(NETWORKS) \ $(DRIVERS) \ $(LIBS) \ -o vmlinux diff -u --recursive --new-file v2.1.31/linux/arch/alpha/Makefile linux/arch/alpha/Makefile --- v2.1.31/linux/arch/alpha/Makefile Thu Feb 6 04:42:34 1997 +++ linux/arch/alpha/Makefile Fri Apr 4 12:02:16 1997 @@ -30,7 +30,7 @@ SUBDIRS := $(SUBDIRS) arch/alpha/kernel arch/alpha/mm arch/alpha/lib \ arch/alpha/math-emu -ARCHIVES := arch/alpha/kernel/kernel.o arch/alpha/mm/mm.o $(ARCHIVES) +CORE_FILES := arch/alpha/kernel/kernel.o arch/alpha/mm/mm.o $(CORE_FILES) LIBS := $(TOPDIR)/arch/alpha/math-emu/math-emu.a \ $(TOPDIR)/arch/alpha/lib/lib.a $(LIBS) $(TOPDIR)/arch/alpha/lib/lib.a diff -u --recursive --new-file v2.1.31/linux/arch/alpha/config.in linux/arch/alpha/config.in --- v2.1.31/linux/arch/alpha/config.in Fri Apr 4 08:52:16 1997 +++ linux/arch/alpha/config.in Sat Apr 5 12:14:20 1997 @@ -91,8 +91,10 @@ fi bool 'Networking support' CONFIG_NET bool 'System V IPC' CONFIG_SYSVIPC +bool 'Sysctl support' CONFIG_SYSCTL tristate 'Kernel support for a.out (ECOFF) binaries' CONFIG_BINFMT_AOUT tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF +tristate 'Kernel support for Linux/Intel ELF binaries' CONFIG_BINFMT_EM86 endmenu source drivers/block/Config.in diff -u --recursive --new-file v2.1.31/linux/arch/alpha/defconfig linux/arch/alpha/defconfig --- v2.1.31/linux/arch/alpha/defconfig Fri Apr 4 08:52:16 1997 +++ linux/arch/alpha/defconfig Sat Apr 5 12:14:20 1997 @@ -39,6 +39,7 @@ CONFIG_TGA_CONSOLE=y CONFIG_NET=y CONFIG_SYSVIPC=y +CONFIG_SYSCTL=y CONFIG_BINFMT_AOUT=y CONFIG_BINFMT_ELF=y diff -u --recursive --new-file v2.1.31/linux/arch/alpha/kernel/entry.S linux/arch/alpha/kernel/entry.S --- v2.1.31/linux/arch/alpha/kernel/entry.S Thu Mar 27 14:39:59 1997 +++ linux/arch/alpha/kernel/entry.S Thu Apr 3 09:59:57 1997 @@ -22,7 +22,7 @@ /* * stack offsets */ -#define SP_OFF 160 +#define SP_OFF 184 #define SWITCH_STACK_SIZE 320 @@ -47,9 +47,11 @@ * regs 9-15 preserved by C code * regs 16-18 saved by PAL-code * regs 29-30 saved and set up by PAL-code + * JRP - Save regs 16-18 in a special area of the stack, so that + * the palcode-provided values are available to the signal handler. */ #define SAVE_ALL \ - subq $30,160,$30; \ + subq $30,184,$30; \ stq $0,0($30); \ stq $1,8($30); \ stq $2,16($30); \ @@ -71,7 +73,10 @@ stq $26,128($30); \ stq $27,136($30); \ stq $28,144($30); \ - stq $2,152($30) + stq $2,152($30); \ + stq $16,160($30); \ + stq $17,168($30); \ + stq $18,176($30) #define RESTORE_ALL \ lda $19,hae; \ @@ -108,7 +113,7 @@ ldq $26,128($30); \ ldq $27,136($30); \ ldq $28,144($30); \ - addq $30,160,$30 + addq $30,184,$30 .text .set noat diff -u --recursive --new-file v2.1.31/linux/arch/alpha/kernel/signal.c linux/arch/alpha/kernel/signal.c --- v2.1.31/linux/arch/alpha/kernel/signal.c Sun Jan 26 04:26:39 1997 +++ linux/arch/alpha/kernel/signal.c Thu Apr 3 10:01:31 1997 @@ -218,6 +218,9 @@ put_user(regs->gp , sc->sc_regs+29); for (i = 0; i < 31; i++) put_user(sw->fp[i], sc->sc_fpregs+i); + put_user(regs->trap_a0, &sc->sc_traparg_a0); + put_user(regs->trap_a1, &sc->sc_traparg_a1); + put_user(regs->trap_a2, &sc->sc_traparg_a2); /* * The following is: diff -u --recursive --new-file v2.1.31/linux/arch/i386/Makefile linux/arch/i386/Makefile --- v2.1.31/linux/arch/i386/Makefile Sun Jan 26 02:07:04 1997 +++ linux/arch/i386/Makefile Fri Apr 4 12:01:53 1997 @@ -70,7 +70,7 @@ HEAD := arch/i386/kernel/head.o SUBDIRS := $(SUBDIRS) arch/i386/kernel arch/i386/mm arch/i386/lib -ARCHIVES := arch/i386/kernel/kernel.o arch/i386/mm/mm.o $(ARCHIVES) +CORE_FILES := arch/i386/kernel/kernel.o arch/i386/mm/mm.o $(CORE_FILES) LIBS := $(TOPDIR)/arch/i386/lib/lib.a $(LIBS) $(TOPDIR)/arch/i386/lib/lib.a ifdef CONFIG_MATH_EMULATION diff -u --recursive --new-file v2.1.31/linux/arch/i386/config.in linux/arch/i386/config.in --- v2.1.31/linux/arch/i386/config.in Wed Jan 15 18:46:06 1997 +++ linux/arch/i386/config.in Sat Apr 5 12:14:20 1997 @@ -31,6 +31,7 @@ fi bool 'MCA support' CONFIG_MCA bool 'System V IPC' CONFIG_SYSVIPC +bool 'Sysctl support' CONFIG_SYSCTL tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then diff -u --recursive --new-file v2.1.31/linux/arch/i386/defconfig linux/arch/i386/defconfig --- v2.1.31/linux/arch/i386/defconfig Fri Apr 4 08:52:16 1997 +++ linux/arch/i386/defconfig Sat Apr 5 12:14:20 1997 @@ -22,6 +22,7 @@ CONFIG_PCI=y # CONFIG_MCA is not set CONFIG_SYSVIPC=y +CONFIG_SYSCTL=y CONFIG_BINFMT_AOUT=y CONFIG_BINFMT_ELF=y # CONFIG_M386 is not set @@ -191,6 +192,8 @@ # CONFIG_UMSDOS_FS is not set CONFIG_PROC_FS=y CONFIG_NFS_FS=y +CONFIG_SUNRPC=y +CONFIG_LOCKD=y # CONFIG_ROOT_NFS is not set # CONFIG_SMB_FS is not set CONFIG_ISO9660_FS=y diff -u --recursive --new-file v2.1.31/linux/arch/i386/kernel/irq.c linux/arch/i386/kernel/irq.c --- v2.1.31/linux/arch/i386/kernel/irq.c Fri Apr 4 08:52:17 1997 +++ linux/arch/i386/kernel/irq.c Fri Apr 4 10:52:19 1997 @@ -40,6 +40,8 @@ static unsigned char cache_21 = 0xff; static unsigned char cache_A1 = 0xff; +unsigned int local_irq_count[NR_CPUS]; + #ifdef __SMP_PROF__ static unsigned int int_count[NR_CPUS][NR_IRQS] = {{0},}; #endif @@ -329,7 +331,6 @@ unsigned char global_irq_holder = NO_PROC_ID; unsigned volatile int global_irq_lock; unsigned volatile int global_irq_count; -unsigned int local_irq_count[NR_CPUS]; #define irq_active(cpu) \ (global_irq_count != local_irq_count[cpu]) @@ -353,11 +354,11 @@ static unsigned long previous_irqholder; #undef INIT_STUCK -#define INIT_STUCK 10000000 +#define INIT_STUCK 100000000 #undef STUCK #define STUCK \ -if (!--stuck) {printk("wait_on_irq stuck at %08lx, waiting for %08lx (local=%d, global=%d)\n", where, previous_irqholder, local_count, global_irq_count); stuck = INIT_STUCK;} +if (!--stuck) {printk("wait_on_irq CPU#%d stuck at %08lx, waiting for %08lx (local=%d, global=%d)\n", cpu, where, previous_irqholder, local_count, global_irq_count); stuck = INIT_STUCK; } static inline void wait_on_irq(int cpu, unsigned long where) { @@ -393,6 +394,28 @@ } } +/* + * This is called when we want to synchronize with + * interrupts. We may for example tell a device to + * stop sending interrupts: but to make sure there + * are no interrupts that are executing on another + * CPU we need to call this function. + * + * On UP this is a no-op. + */ +void synchronize_irq(void) +{ + int cpu = smp_processor_id(); + int local_count = local_irq_count[cpu]; + + /* Do we need to wait? */ + if (local_count != global_irq_count) { + /* The stupid way to do this */ + cli(); + sti(); + } +} + #undef INIT_STUCK #define INIT_STUCK 10000000 @@ -436,7 +459,7 @@ int cpu = smp_processor_id(); unsigned long where; - __asm__("movl 12(%%esp),%0":"=r" (where)); + __asm__("movl 16(%%esp),%0":"=r" (where)); __cli(); get_irqlock(cpu, where); } diff -u --recursive --new-file v2.1.31/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c --- v2.1.31/linux/arch/i386/kernel/smp.c Thu Mar 27 14:39:59 1997 +++ linux/arch/i386/kernel/smp.c Fri Apr 4 12:37:26 1997 @@ -1232,8 +1232,10 @@ void smp_flush_tlb(void) { unsigned long flags; - if(smp_activated && smp_processor_id()!=active_kernel_processor) - panic("CPU #%d:Attempted flush tlb IPI when not AKP(=%d)\n",smp_processor_id(),active_kernel_processor); + if(smp_activated && smp_processor_id()!=active_kernel_processor) { + printk("CPU #%d:Attempted flush tlb IPI when not AKP(=%d)\n",smp_processor_id(),active_kernel_processor); + *(char *)0=0; + } /* printk("SMI-");*/ /* diff -u --recursive --new-file v2.1.31/linux/arch/i386/kernel/time.c linux/arch/i386/kernel/time.c --- v2.1.31/linux/arch/i386/kernel/time.c Fri Apr 4 08:52:17 1997 +++ linux/arch/i386/kernel/time.c Thu Apr 3 10:13:39 1997 @@ -454,7 +454,8 @@ )*60 + sec; /* finally seconds */ } -static unsigned long get_cmos_time(void) +/* not static: needed by APM */ +unsigned long get_cmos_time(void) { unsigned int year, mon, day, hour, min, sec; int i; diff -u --recursive --new-file v2.1.31/linux/arch/i386/kernel/traps.c linux/arch/i386/kernel/traps.c --- v2.1.31/linux/arch/i386/kernel/traps.c Thu Mar 27 14:39:59 1997 +++ linux/arch/i386/kernel/traps.c Thu Apr 3 10:35:53 1997 @@ -114,7 +114,7 @@ #define VMALLOC_OFFSET (8*1024*1024) #define MODULE_RANGE (8*1024*1024) -/*static*/ void die_if_kernel(const char * str, struct pt_regs * regs, long err) +static void show_regs(struct pt_regs *regs) { int i; unsigned long esp; @@ -130,8 +130,6 @@ esp = regs->esp; ss = regs->xss & 0xffff; } - console_verbose(); - printk("%s: %04lx\n", str, err & 0xffff); printk("CPU: %d\n", smp_processor_id()); printk("EIP: %04x:[<%08lx>]\nEFLAGS: %08lx\n", 0xffff & regs->xcs,regs->eip,regs->eflags); printk("eax: %08lx ebx: %08lx ecx: %08lx edx: %08lx\n", @@ -182,6 +180,13 @@ for(i=0;i<20;i++) printk("%02x ",0xff & get_seg_byte(regs->xcs & 0xffff,(i+(char *)regs->eip))); printk("\n"); +} + +/*static*/ void die_if_kernel(const char * str, struct pt_regs * regs, long err) +{ + console_verbose(); + printk("%s: %04lx\n", str, err & 0xffff); + show_regs(regs); do_exit(SIGSEGV); } @@ -231,6 +236,7 @@ asmlinkage void do_nmi(struct pt_regs * regs, long error_code) { + printk("NMI\n"); show_regs(regs); #ifdef CONFIG_SMP_NMI_INVAL smp_flush_tlb_rcv(); #else diff -u --recursive --new-file v2.1.31/linux/arch/m68k/config.in linux/arch/m68k/config.in --- v2.1.31/linux/arch/m68k/config.in Fri Apr 4 08:52:17 1997 +++ linux/arch/m68k/config.in Sat Apr 5 12:14:20 1997 @@ -49,6 +49,7 @@ bool 'Networking support' CONFIG_NET bool 'System V IPC' CONFIG_SYSVIPC +bool 'Sysctl support' CONFIG_SYSCTL tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF if [ "$CONFIG_AMIGA" = "y" ]; then diff -u --recursive --new-file v2.1.31/linux/arch/m68k/defconfig linux/arch/m68k/defconfig --- v2.1.31/linux/arch/m68k/defconfig Fri Apr 4 08:52:17 1997 +++ linux/arch/m68k/defconfig Sat Apr 5 12:14:20 1997 @@ -38,6 +38,7 @@ # CONFIG_NET=y CONFIG_SYSVIPC=y +CONFIG_SYSCTL=y CONFIG_BINFMT_AOUT=y CONFIG_BINFMT_ELF=y CONFIG_ZORRO=y diff -u --recursive --new-file v2.1.31/linux/arch/mips/config.in linux/arch/mips/config.in --- v2.1.31/linux/arch/mips/config.in Sat May 4 23:05:58 1996 +++ linux/arch/mips/config.in Sat Apr 5 12:14:20 1997 @@ -50,6 +50,7 @@ # fi #fi bool 'System V IPC' CONFIG_SYSVIPC +bool 'Sysctl support' CONFIG_SYSCTL endmenu mainmenu_option next_comment diff -u --recursive --new-file v2.1.31/linux/arch/mips/defconfig linux/arch/mips/defconfig --- v2.1.31/linux/arch/mips/defconfig Fri Apr 4 08:52:17 1997 +++ linux/arch/mips/defconfig Sat Apr 5 12:14:20 1997 @@ -26,6 +26,7 @@ CONFIG_CPU_LITTLE_ENDIAN=y # CONFIG_NET is not set # CONFIG_SYSVIPC is not set +CONFIG_SYSCTL=y # # Loadable module support diff -u --recursive --new-file v2.1.31/linux/arch/ppc/config.in linux/arch/ppc/config.in --- v2.1.31/linux/arch/ppc/config.in Wed Jan 15 18:46:06 1997 +++ linux/arch/ppc/config.in Sat Apr 5 12:14:20 1997 @@ -30,6 +30,7 @@ fi fi bool 'System V IPC' CONFIG_SYSVIPC +bool 'Sysctl support' CONFIG_SYSCTL tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT tristate 'Kernel support for JAVA binaries' CONFIG_BINFMT_JAVA tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF diff -u --recursive --new-file v2.1.31/linux/arch/sparc/Makefile linux/arch/sparc/Makefile --- v2.1.31/linux/arch/sparc/Makefile Mon Mar 17 14:54:20 1997 +++ linux/arch/sparc/Makefile Fri Apr 4 12:02:44 1997 @@ -26,14 +26,14 @@ SUBDIRS := $(SUBDIRS) arch/sparc/kernel arch/sparc/lib arch/sparc/mm \ arch/sparc/prom -ARCHIVES := arch/sparc/kernel/kernel.o arch/sparc/mm/mm.o $(ARCHIVES) +CORE_FILES := arch/sparc/kernel/kernel.o arch/sparc/mm/mm.o $(CORE_FILES) LIBS := $(TOPDIR)/lib/lib.a $(LIBS) $(TOPDIR)/arch/sparc/prom/promlib.a \ $(TOPDIR)/arch/sparc/lib/lib.a ifdef CONFIG_AP1000 SUBDIRS := $(SUBDIRS) arch/sparc/ap1000 mpp -ARCHIVES := $(TOPDIR)/arch/sparc/ap1000/ap1000lib.o $(TOPDIR)/mpp/mpplib.o $(ARCHIVES) +CORE_FILES := $(TOPDIR)/arch/sparc/ap1000/ap1000lib.o $(TOPDIR)/mpp/mpplib.o $(CORE_FILES) DRIVERS := $(DRIVERS) drivers/ap1000/ap1000.a CFLAGS := $(CFLAGS) -D__MPP__=1 endif diff -u --recursive --new-file v2.1.31/linux/arch/sparc/config.in linux/arch/sparc/config.in --- v2.1.31/linux/arch/sparc/config.in Fri Apr 4 08:52:17 1997 +++ linux/arch/sparc/config.in Sat Apr 5 12:14:20 1997 @@ -50,6 +50,7 @@ tristate 'Openprom tree appears in /proc/openprom (EXPERIMENTAL)' CONFIG_SUN_OPENPROMFS bool 'Networking support' CONFIG_NET bool 'System V IPC' CONFIG_SYSVIPC +bool 'Sysctl support' CONFIG_SYSCTL tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then diff -u --recursive --new-file v2.1.31/linux/arch/sparc/defconfig linux/arch/sparc/defconfig --- v2.1.31/linux/arch/sparc/defconfig Fri Apr 4 08:52:17 1997 +++ linux/arch/sparc/defconfig Sat Apr 5 12:14:20 1997 @@ -59,6 +59,7 @@ CONFIG_SUN_OPENPROMFS=m CONFIG_NET=y CONFIG_SYSVIPC=y +CONFIG_SYSCTL=y CONFIG_BINFMT_AOUT=y CONFIG_BINFMT_ELF=y CONFIG_BINFMT_JAVA=m diff -u --recursive --new-file v2.1.31/linux/arch/sparc64/Makefile linux/arch/sparc64/Makefile --- v2.1.31/linux/arch/sparc64/Makefile Thu Mar 27 14:40:00 1997 +++ linux/arch/sparc64/Makefile Fri Apr 4 12:03:15 1997 @@ -32,7 +32,7 @@ SUBDIRS := $(SUBDIRS) arch/sparc64/kernel arch/sparc64/lib arch/sparc64/mm \ arch/sparc64/prom -ARCHIVES := arch/sparc64/kernel/kernel.o arch/sparc64/mm/mm.o $(ARCHIVES) +CORE_FILES := arch/sparc64/kernel/kernel.o arch/sparc64/mm/mm.o $(CORE_FILES) LIBS := $(TOPDIR)/lib/lib.a $(LIBS) $(TOPDIR)/arch/sparc64/prom/promlib.a \ $(TOPDIR)/arch/sparc64/lib/lib.a diff -u --recursive --new-file v2.1.31/linux/arch/sparc64/config.in linux/arch/sparc64/config.in --- v2.1.31/linux/arch/sparc64/config.in Fri Apr 4 08:52:17 1997 +++ linux/arch/sparc64/config.in Sat Apr 5 12:14:20 1997 @@ -49,6 +49,7 @@ tristate 'Openprom tree appears in /proc/openprom (EXPERIMENTAL)' CONFIG_SUN_OPENPROMFS bool 'Networking support' CONFIG_NET bool 'System V IPC' CONFIG_SYSVIPC +bool 'Sysctl support' CONFIG_SYSCTL tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then diff -u --recursive --new-file v2.1.31/linux/arch/sparc64/defconfig linux/arch/sparc64/defconfig --- v2.1.31/linux/arch/sparc64/defconfig Fri Apr 4 08:52:17 1997 +++ linux/arch/sparc64/defconfig Sat Apr 5 12:14:20 1997 @@ -59,6 +59,7 @@ CONFIG_SUN_OPENPROMFS=m CONFIG_NET=y CONFIG_SYSVIPC=y +CONFIG_SYSCTL=y CONFIG_BINFMT_AOUT=y CONFIG_BINFMT_ELF=y CONFIG_BINFMT_JAVA=m diff -u --recursive --new-file v2.1.31/linux/drivers/block/genhd.c linux/drivers/block/genhd.c --- v2.1.31/linux/drivers/block/genhd.c Fri Apr 4 08:52:17 1997 +++ linux/drivers/block/genhd.c Thu Apr 3 10:23:56 1997 @@ -24,9 +24,7 @@ #include #include #include -#ifdef CONFIG_BLK_DEV_INITRD #include -#endif #include @@ -109,6 +107,52 @@ SYS_IND(p) == LINUX_EXTENDED_PARTITION); } +static unsigned int get_ptable_blocksize(kdev_t dev) +{ + int ret = 1024; + + /* + * See whether the low-level driver has given us a minumum blocksize. + * If so, check to see whether it is larger than the default of 1024. + */ + if (!blksize_size[MAJOR(dev)]) + { + return ret; + } + + /* + * Check for certain special power of two sizes that we allow. + * With anything larger than 1024, we must force the blocksize up to + * the natural blocksize for the device so that we don't have to try + * and read partial sectors. Anything smaller should be just fine. + */ + switch( blksize_size[MAJOR(dev)][MINOR(dev)] ) + { + case 2048: + ret = 2048; + break; + case 4096: + ret = 4096; + break; + case 8192: + ret = 8192; + break; + case 1024: + case 512: + case 256: + case 0: + /* + * These are all OK. + */ + break; + default: + panic("Strange blocksize for partition table\n"); + } + + return ret; + +} + #ifdef CONFIG_MSDOS_PARTITION /* * Create devices for each logical partition in an extended partition. @@ -138,7 +182,7 @@ while (1) { if ((current_minor & mask) == 0) return; - if (!(bh = bread(dev,0,1024))) + if (!(bh = bread(dev,0,get_ptable_blocksize(dev)))) return; /* * This block is from a device that we're about to stomp on. @@ -222,7 +266,7 @@ struct bsd_partition *p; int mask = (1 << hd->minor_shift) - 1; - if (!(bh = bread(dev,0,1024))) + if (!(bh = bread(dev,0,get_ptable_blocksize(dev)))) return; bh->b_state = 0; l = (struct bsd_disklabel *) (bh->b_data+512); @@ -259,7 +303,7 @@ read_mbr: #endif - if (!(bh = bread(dev,0,1024))) { + if (!(bh = bread(dev,0,get_ptable_blocksize(dev)))) { printk(" unable to read partition table\n"); return -1; } @@ -438,7 +482,7 @@ struct d_partition * partition; #define DISKLABELMAGIC (0x82564557UL) - if (!(bh = bread(dev,0,1024))) { + if (!(bh = bread(dev,0,get_ptable_blocksize(dev)))) { printk("unable to read partition table\n"); return -1; } @@ -501,7 +545,7 @@ unsigned long spc; #define SUN_LABEL_MAGIC 0xDABE - if(!(bh = bread(dev, 0, 1024))) { + if(!(bh = bread(dev, 0, get_ptable_blocksize(dev)))) { printk("Dev %s: unable to read partition table\n", kdevname(dev)); return -1; diff -u --recursive --new-file v2.1.31/linux/drivers/block/ide-cd.h linux/drivers/block/ide-cd.h --- v2.1.31/linux/drivers/block/ide-cd.h Wed Dec 11 06:45:54 1996 +++ linux/drivers/block/ide-cd.h Fri Apr 4 08:49:31 1997 @@ -15,15 +15,6 @@ #endif -/* Turn this on to have the driver print out the meanings of the - ATAPI error codes. This will use up additional kernel-space - memory, though. */ - -#ifndef VERBOSE_IDE_CD_ERRORS -#define VERBOSE_IDE_CD_ERRORS 0 -#endif - - /* Turning this on will remove code to work around various nonstandard ATAPI implementations. If you know your drive follows the standard, this will give you a slightly smaller kernel. */ diff -u --recursive --new-file v2.1.31/linux/drivers/char/lp.c linux/drivers/char/lp.c --- v2.1.31/linux/drivers/char/lp.c Fri Apr 4 08:52:19 1997 +++ linux/drivers/char/lp.c Thu Apr 3 19:15:44 1997 @@ -117,6 +117,8 @@ struct lp_stats *stats; do { + if(need_resched) + schedule(); if ((status = LP_S(minor)) & LP_PBUSY) { if (!LP_CAREFUL_READY(minor, status)) return 0; diff -u --recursive --new-file v2.1.31/linux/drivers/char/vga.c linux/drivers/char/vga.c --- v2.1.31/linux/drivers/char/vga.c Sun Jan 26 02:07:17 1997 +++ linux/drivers/char/vga.c Thu Apr 3 10:55:14 1997 @@ -115,14 +115,10 @@ void __set_origin(unsigned short offset) { - unsigned long flags; - clear_selection(); - save_flags(flags); cli(); __origin = offset; write_vga(12, offset); - restore_flags(flags); } /* @@ -140,18 +136,14 @@ void set_cursor(int currcons) { - unsigned long flags; - if (currcons != fg_console || console_blanked || vcmode == KD_GRAPHICS) return; if (__real_origin != __origin) __set_origin(__real_origin); - save_flags(flags); cli(); if (deccm) { write_vga(14, (pos - video_mem_base)>>1); } else hide_cursor(); - restore_flags(flags); } unsigned long diff -u --recursive --new-file v2.1.31/linux/drivers/net/cs89x0.c linux/drivers/net/cs89x0.c --- v2.1.31/linux/drivers/net/cs89x0.c Fri Apr 4 08:52:20 1997 +++ linux/drivers/net/cs89x0.c Fri Apr 4 14:02:05 1997 @@ -758,7 +758,7 @@ short ioaddr = dev->base_addr; unsigned long flags; - if (net_debug > 3)printk("%s: sent %ld byte packet of type %x\n", dev->name, skb->len, (skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]); + if (net_debug > 3)printk("%s: sent %d byte packet of type %x\n", dev->name, skb->len, (skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]); /* keep the upload from being interrupted, since we ask the chip to start transmitting before the diff -u --recursive --new-file v2.1.31/linux/drivers/net/de4x5.c linux/drivers/net/de4x5.c --- v2.1.31/linux/drivers/net/de4x5.c Fri Apr 4 08:52:20 1997 +++ linux/drivers/net/de4x5.c Thu Apr 3 16:37:22 1997 @@ -1359,8 +1359,9 @@ if (dev->interrupt) printk("%s: Re-entering the interrupt handler.\n", dev->name); - + DISABLE_IRQs; /* Ensure non re-entrancy */ + synchronize_irq(); dev->interrupt = MASK_INTERRUPTS; for (limit=0; limit<8; limit++) { @@ -1389,13 +1390,11 @@ dev->name, sts); return; } -if (sts & (STS_AIS | STS_UNF | STS_SE | STS_LNF | STS_RWT | STS_RU | STS_TJT)) - printk("STS=%08x\n",sts); } /* Load the TX ring with any locally stored packets */ if (!set_bit(0, (void *)&lp->cache.lock)) { - while (lp->cache.skb && !dev->tbusy && lp->tx_enable) { + if (lp->cache.skb && !dev->tbusy && lp->tx_enable) { de4x5_queue_pkt(de4x5_get_cache(dev), dev); } lp->cache.lock = 0; diff -u --recursive --new-file v2.1.31/linux/drivers/net/shaper.c linux/drivers/net/shaper.c --- v2.1.31/linux/drivers/net/shaper.c Fri Apr 4 08:52:21 1997 +++ linux/drivers/net/shaper.c Fri Apr 4 11:58:44 1997 @@ -614,7 +614,7 @@ if(err<0) return err; printk(SHAPER_BANNER); - if (register_netdev(dev) != 0) + if (register_netdev(&dev_shape) != 0) return -EIO; printk("Traffic shaper initialised.\n"); return 0; diff -u --recursive --new-file v2.1.31/linux/drivers/scsi/AM53C974.c linux/drivers/scsi/AM53C974.c --- v2.1.31/linux/drivers/scsi/AM53C974.c Tue Mar 4 10:25:23 1997 +++ linux/drivers/scsi/AM53C974.c Fri Apr 4 14:03:29 1997 @@ -117,6 +117,343 @@ #define INSIDE(x,l,h) ( ((x) >= (l)) && ((x) <= (h)) ) + +#include + +/*************************************************************************************** +* Default setting of the controller's SCSI id. Edit and uncomment this only if your * +* BIOS does not correctly initialize the controller's SCSI id. * +* If you don't get a warning during boot, it is correctly initialized. * +****************************************************************************************/ +/* #define AM53C974_SCSI_ID 7 */ + +/*************************************************************************************** +* Default settings for sync. negotiation enable, transfer rate and sync. offset. * +* These settings can be replaced by LILO overrides (append) with the following syntax: * +* AM53C974=host-scsi-id, target-scsi-id, max-rate, max-offset * +* Sync. negotiation is disabled by default and will be enabled for those targets which * +* are specified in the LILO override * +****************************************************************************************/ +#define DEFAULT_SYNC_NEGOTIATION_ENABLED 0 /* 0 or 1 */ +#define DEFAULT_RATE 5 /* MHz, min: 3; max: 10 */ +#define DEFAULT_SYNC_OFFSET 0 /* bytes, min: 0; max: 15; use 0 for async. mode */ + +/*************************************************************************************** +* If defined, don't allow targets to disconnect during commands. This will reduce * +* performance, but may be worthwhile if you suspect the driver of corrupting data when * +* a disconnect happens. * +***************************************************************************************/ +#define AM53C974_PROHIBIT_DISCONNECT + +/* --------------------- don't edit below here --------------------- */ + +#define AM53C974_DRIVER_REVISION_MAJOR 0 +#define AM53C974_DRIVER_REVISION_MINOR 5 +#define SEPARATOR_LINE \ +"--------------------------------------------------------------------------\n" + +/* debug control */ +/* #define AM53C974_DEBUG */ +/* #define AM53C974_DEBUG_MSG */ +/* #define AM53C974_DEBUG_KEYWAIT */ +/* #define AM53C974_DEBUG_INIT */ +/* #define AM53C974_DEBUG_QUEUE */ +/* #define AM53C974_DEBUG_INFO */ +/* #define AM53C974_DEBUG_LINKED */ +/* #define VERBOSE_AM53C974_DEBUG */ +/* #define AM53C974_DEBUG_INTR */ +/* #define AM53C974_DEB_RESEL */ +#define AM53C974_DEBUG_ABORT +/* #define AM53C974_OPTION_DEBUG_PROBE_ONLY */ + +/* special options/constants */ +#define DEF_CLK 40 /* chip clock freq. in MHz */ +#define MIN_PERIOD 4 /* for negotiation: min. number of clocks per cycle */ +#define MAX_PERIOD 13 /* for negotiation: max. number of clocks per cycle */ +#define MAX_OFFSET 15 /* for negotiation: max. offset (0=async) */ + +#define DEF_SCSI_TIMEOUT 245 /* STIMREG value, 40 Mhz */ +#define DEF_STP 8 /* STPREG value assuming 5.0 MB/sec, FASTCLK, FASTSCSI */ +#define DEF_SOF_RAD 0 /* REQ/ACK deassertion delay */ +#define DEF_SOF_RAA 0 /* REQ/ACK assertion delay */ +#define DEF_ETM 0 /* CNTLREG1, ext. timing mode */ +#define DEF_PERE 1 /* CNTLREG1, parity error reporting */ +#define DEF_CLKF 0 /* CLKFREG, 0=40 Mhz */ +#define DEF_ENF 1 /* CNTLREG2, enable features */ +#define DEF_ADIDCHK 0 /* CNTLREG3, additional ID check */ +#define DEF_FASTSCSI 1 /* CNTLREG3, fast SCSI */ +#define DEF_FASTCLK 1 /* CNTLREG3, fast clocking, 5 MB/sec at 40MHz chip clk */ +#define DEF_GLITCH 1 /* CNTLREG4, glitch eater, 0=12ns, 1=35ns, 2=25ns, 3=off */ +#define DEF_PWD 0 /* CNTLREG4, reduced power feature */ +#define DEF_RAE 0 /* CNTLREG4, RAE active negation on REQ, ACK only */ +#define DEF_RADE 1 /* 1CNTLREG4, active negation on REQ, ACK and data */ + +/*** PCI block ***/ +/* standard registers are defined in */ +#ifndef PCI_VENDOR_ID_AMD +#define PCI_VENDOR_ID_AMD 0x1022 +#define PCI_DEVICE_ID_AMD_SCSI 0x2020 +#endif +#define PCI_BASE_MASK 0xFFFFFFE0 +#define PCI_COMMAND_PERREN 0x40 +#define PCI_SCRATCH_REG_0 0x40 /* 16 bits */ +#define PCI_SCRATCH_REG_1 0x42 /* 16 bits */ +#define PCI_SCRATCH_REG_2 0x44 /* 16 bits */ +#define PCI_SCRATCH_REG_3 0x46 /* 16 bits */ +#define PCI_SCRATCH_REG_4 0x48 /* 16 bits */ +#define PCI_SCRATCH_REG_5 0x4A /* 16 bits */ +#define PCI_SCRATCH_REG_6 0x4C /* 16 bits */ +#define PCI_SCRATCH_REG_7 0x4E /* 16 bits */ + +/*** SCSI block ***/ +#define CTCLREG 0x00 /* r current transf. count, low byte */ +#define CTCMREG 0x04 /* r current transf. count, middle byte */ +#define CTCHREG 0x38 /* r current transf. count, high byte */ +#define STCLREG 0x00 /* w start transf. count, low byte */ +#define STCMREG 0x04 /* w start transf. count, middle byte */ +#define STCHREG 0x38 /* w start transf. count, high byte */ +#define FFREG 0x08 /* rw SCSI FIFO reg. */ +#define STIMREG 0x14 /* w SCSI timeout reg. */ + +#define SDIDREG 0x10 /* w SCSI destination ID reg. */ +#define SDIREG_MASK 0x07 /* mask */ + +#define STPREG 0x18 /* w synchronous transf. period reg. */ +#define STPREG_STP 0x1F /* synchr. transfer period */ + +#define CLKFREG 0x24 /* w clock factor reg. */ +#define CLKFREG_MASK 0x07 /* mask */ + +#define CMDREG 0x0C /* rw SCSI command reg. */ +#define CMDREG_DMA 0x80 /* set DMA mode (set together with opcodes below) */ +#define CMDREG_IT 0x10 /* information transfer */ +#define CMDREG_ICCS 0x11 /* initiator command complete steps */ +#define CMDREG_MA 0x12 /* message accepted */ +#define CMDREG_TPB 0x98 /* transfer pad bytes, DMA mode only */ +#define CMDREG_SATN 0x1A /* set ATN */ +#define CMDREG_RATN 0x1B /* reset ATN */ +#define CMDREG_SOAS 0x41 /* select without ATN steps */ +#define CMDREG_SAS 0x42 /* select with ATN steps (1 msg byte) */ +#define CMDREG_SASS 0x43 /* select with ATN and stop steps */ +#define CMDREG_ESR 0x44 /* enable selection/reselection */ +#define CMDREG_DSR 0x45 /* disable selection/reselection */ +#define CMDREG_SA3S 0x46 /* select with ATN 3 steps (3 msg bytes) */ +#define CMDREG_NOP 0x00 /* no operation */ +#define CMDREG_CFIFO 0x01 /* clear FIFO */ +#define CMDREG_RDEV 0x02 /* reset device */ +#define CMDREG_RBUS 0x03 /* reset SCSI bus */ + +#define STATREG 0x10 /* r SCSI status reg. */ +#define STATREG_INT 0x80 /* SCSI interrupt condition detected */ +#define STATREG_IOE 0x40 /* SCSI illegal operation error detected */ +#define STATREG_PE 0x20 /* SCSI parity error detected */ +#define STATREG_CTZ 0x10 /* CTC reg decremented to zero */ +#define STATREG_MSG 0x04 /* SCSI MSG phase (latched?) */ +#define STATREG_CD 0x02 /* SCSI C/D phase (latched?) */ +#define STATREG_IO 0x01 /* SCSI I/O phase (latched?) */ +#define STATREG_PHASE 0x07 /* SCSI phase mask */ + +#define INSTREG 0x14 /* r interrupt status reg. */ +#define INSTREG_SRST 0x80 /* SCSI reset detected */ +#define INSTREG_ICMD 0x40 /* SCSI invalid command detected */ +#define INSTREG_DIS 0x20 /* target disconnected or sel/resel timeout*/ +#define INSTREG_SR 0x10 /* device on bus has service request */ +#define INSTREG_SO 0x08 /* successful operation */ +#define INSTREG_RESEL 0x04 /* device reselected as initiator */ + +#define ISREG 0x18 /* r internal state reg. */ +#define ISREG_SOF 0x08 /* synchronous offset flag (act. low) */ +#define ISREG_IS 0x07 /* status of intermediate op. */ +#define ISREG_OK_NO_STOP 0x04 /* selection successful */ +#define ISREG_OK_STOP 0x01 /* selection successful */ + +#define CFIREG 0x1C /* r current FIFO/internal state reg. */ +#define CFIREG_IS 0xE0 /* status of intermediate op. */ +#define CFIREG_CF 0x1F /* number of bytes in SCSI FIFO */ + +#define SOFREG 0x1C /* w synchr. offset reg. */ +#define SOFREG_RAD 0xC0 /* REQ/ACK deassertion delay (sync.) */ +#define SOFREG_RAA 0x30 /* REQ/ACK assertion delay (sync.) */ +#define SOFREG_SO 0x0F /* synch. offset (sync.) */ + +#define CNTLREG1 0x20 /* rw control register one */ +#define CNTLREG1_ETM 0x80 /* set extended timing mode */ +#define CNTLREG1_DISR 0x40 /* disable interrupt on SCSI reset */ +#define CNTLREG1_PERE 0x10 /* enable parity error reporting */ +#define CNTLREG1_SID 0x07 /* host adapter SCSI ID */ + +#define CNTLREG2 0x2C /* rw control register two */ +#define CNTLREG2_ENF 0x40 /* enable features */ + +#define CNTLREG3 0x30 /* rw control register three */ +#define CNTLREG3_ADIDCHK 0x80 /* additional ID check */ +#define CNTLREG3_FASTSCSI 0x10 /* fast SCSI */ +#define CNTLREG3_FASTCLK 0x08 /* fast SCSI clocking */ + +#define CNTLREG4 0x34 /* rw control register four */ +#define CNTLREG4_GLITCH 0xC0 /* glitch eater */ +#define CNTLREG4_PWD 0x20 /* reduced power feature */ +#define CNTLREG4_RAE 0x08 /* write only, active negot. ctrl. */ +#define CNTLREG4_RADE 0x04 /* active negot. ctrl. */ +#define CNTLREG4_RES 0x10 /* reserved bit, must be 1 */ + +/*** DMA block ***/ +#define DMACMD 0x40 /* rw command */ +#define DMACMD_DIR 0x80 /* transfer direction (1=read from device) */ +#define DMACMD_INTE_D 0x40 /* DMA transfer interrupt enable */ +#define DMACMD_INTE_P 0x20 /* page transfer interrupt enable */ +#define DMACMD_MDL 0x10 /* map to memory descriptor list */ +#define DMACMD_DIAG 0x04 /* diagnostics, set to 0 */ +#define DMACMD_IDLE 0x00 /* idle cmd */ +#define DMACMD_BLAST 0x01 /* flush FIFO to memory */ +#define DMACMD_ABORT 0x02 /* terminate DMA */ +#define DMACMD_START 0x03 /* start DMA */ + +#define DMASTATUS 0x54 /* r status register */ +#define DMASTATUS_BCMPLT 0x20 /* BLAST complete */ +#define DMASTATUS_SCSIINT 0x10 /* SCSI interrupt pending */ +#define DMASTATUS_DONE 0x08 /* DMA transfer terminated */ +#define DMASTATUS_ABORT 0x04 /* DMA transfer aborted */ +#define DMASTATUS_ERROR 0x02 /* DMA transfer error */ +#define DMASTATUS_PWDN 0x02 /* power down indicator */ + +#define DMASTC 0x44 /* rw starting transfer count */ +#define DMASPA 0x48 /* rw starting physical address */ +#define DMAWBC 0x4C /* r working byte counter */ +#define DMAWAC 0x50 /* r working address counter */ +#define DMASMDLA 0x58 /* rw starting MDL address */ +#define DMAWMAC 0x5C /* r working MDL counter */ + +/*** SCSI phases ***/ +#define PHASE_MSGIN 0x07 +#define PHASE_MSGOUT 0x06 +#define PHASE_RES_1 0x05 +#define PHASE_RES_0 0x04 +#define PHASE_STATIN 0x03 +#define PHASE_CMDOUT 0x02 +#define PHASE_DATAIN 0x01 +#define PHASE_DATAOUT 0x00 + + +#define AM53C974_local_declare() unsigned long io_port +#define AM53C974_setio(instance) io_port = instance->io_port +#define AM53C974_read_8(addr) inb(io_port + (addr)) +#define AM53C974_write_8(addr,x) outb((x), io_port + (addr)) +#define AM53C974_read_16(addr) inw(io_port + (addr)) +#define AM53C974_write_16(addr,x) outw((x), io_port + (addr)) +#define AM53C974_read_32(addr) inl(io_port + (addr)) +#define AM53C974_write_32(addr,x) outl((x), io_port + (addr)) + +#define AM53C974_poll_int() { do { statreg = AM53C974_read_8(STATREG); } \ + while (!(statreg & STATREG_INT)) ; \ + AM53C974_read_8(INSTREG) ; } /* clear int */ +#define AM53C974_cfifo() (AM53C974_read_8(CFIREG) & CFIREG_CF) + +/* These are "special" values for the tag parameter passed to AM53C974_select. */ +#define TAG_NEXT -1 /* Use next free tag */ +#define TAG_NONE -2 /* Establish I_T_L nexus instead of I_T_L_Q + * even on SCSI-II devices */ + +/************ LILO overrides *************/ +typedef struct _override_t { + int host_scsi_id; /* SCSI id of the bus controller */ + int target_scsi_id; /* SCSI id of target */ + int max_rate; /* max. transfer rate */ + int max_offset; /* max. sync. offset, 0 = asynchronous */ + } override_t; + +/************ PCI stuff *************/ +#define AM53C974_PCIREG_OPEN() outb(0xF1, 0xCF8); outb(0, 0xCFA) +#define AM53C974_PCIREG_CLOSE() outb(0, 0xCF8) +#define AM53C974_PCIREG_READ_BYTE(instance,a) ( inb((a) + (instance)->io_port) ) +#define AM53C974_PCIREG_READ_WORD(instance,a) ( inw((a) + (instance)->io_port) ) +#define AM53C974_PCIREG_READ_DWORD(instance,a) ( inl((a) + (instance)->io_port) ) +#define AM53C974_PCIREG_WRITE_BYTE(instance,x,a) ( outb((x), (a) + (instance)->io_port) ) +#define AM53C974_PCIREG_WRITE_WORD(instance,x,a) ( outw((x), (a) + (instance)->io_port) ) +#define AM53C974_PCIREG_WRITE_DWORD(instance,x,a) ( outl((x), (a) + (instance)->io_port) ) + +typedef struct _pci_config_t { + /* start of official PCI config space header */ + union { + unsigned int device_vendor; + struct { + unsigned short vendor; + unsigned short device; + } dv; + } dv_id; +#define _device_vendor dv_id.device_vendor +#define _vendor dv_id.dv.vendor +#define _device dv_id.dv.device + union { + unsigned int status_command; + struct { + unsigned short command; + unsigned short status; + } sc; + } stat_cmd; +#define _status_command stat_cmd.status_command +#define _command stat_cmd.sc.command +#define _status stat_cmd.sc.status + union { + unsigned int class_revision; + struct { + unsigned char rev_id; + unsigned char prog_if; + unsigned char sub_class; + unsigned char base_class; + } cr; + } class_rev; +#define _class_revision class_rev.class_revision +#define _rev_id class_rev.cr.rev_id +#define _prog_if class_rev.cr.prog_if +#define _sub_class class_rev.cr.sub_class +#define _base_class class_rev.cr.base_class + union { + unsigned int bist_header_latency_cache; + struct { + unsigned char cache_line_size; + unsigned char latency_timer; + unsigned char header_type; + unsigned char bist; + } bhlc; + } bhlc; +#define _bist_header_latency_cache bhlc.bist_header_latency_cache +#define _cache_line_size bhlc.bhlc.cache_line_size +#define _latency_timer bhlc.bhlc.latency_timer +#define _header_type bhlc.bhlc.header_type +#define _bist bhlc.bhlc.bist + unsigned int _base0; + unsigned int _base1; + unsigned int _base2; + unsigned int _base3; + unsigned int _base4; + unsigned int _base5; + unsigned int rsvd1; + unsigned int rsvd2; + unsigned int _baserom; + unsigned int rsvd3; + unsigned int rsvd4; + union { + unsigned int max_min_ipin_iline; + struct { + unsigned char int_line; + unsigned char int_pin; + unsigned char min_gnt; + unsigned char max_lat; + } mmii; + } mmii; +#define _max_min_ipin_iline mmii.max_min_ipin_iline +#define _int_line mmii.mmii.int_line +#define _int_pin mmii.mmii.int_pin +#define _min_gnt mmii.mmii.min_gnt +#define _max_lat mmii.mmii.max_lat + /* end of official PCI config space header */ + unsigned short _ioaddr; /* config type 1 - private I/O addr */ + unsigned int _pcibus; /* config type 2 - private bus id */ + unsigned int _cardnum; /* config type 2 - private card number */ +} pci_config_t; + + #ifdef AM53C974_DEBUG static void AM53C974_print_pci(struct Scsi_Host *instance); static void AM53C974_print_phase(struct Scsi_Host *instance); diff -u --recursive --new-file v2.1.31/linux/drivers/scsi/AM53C974.h linux/drivers/scsi/AM53C974.h --- v2.1.31/linux/drivers/scsi/AM53C974.h Mon Jan 27 00:42:54 1997 +++ linux/drivers/scsi/AM53C974.h Fri Apr 4 14:03:29 1997 @@ -27,222 +27,6 @@ #ifndef AM53C974_H #define AM53C974_H -#include - -/*************************************************************************************** -* Default setting of the controller's SCSI id. Edit and uncomment this only if your * -* BIOS does not correctly initialize the controller's SCSI id. * -* If you don't get a warning during boot, it is correctly initialized. * -****************************************************************************************/ -/* #define AM53C974_SCSI_ID 7 */ - -/*************************************************************************************** -* Default settings for sync. negotiation enable, transfer rate and sync. offset. * -* These settings can be replaced by LILO overrides (append) with the following syntax: * -* AM53C974=host-scsi-id, target-scsi-id, max-rate, max-offset * -* Sync. negotiation is disabled by default and will be enabled for those targets which * -* are specified in the LILO override * -****************************************************************************************/ -#define DEFAULT_SYNC_NEGOTIATION_ENABLED 0 /* 0 or 1 */ -#define DEFAULT_RATE 5 /* MHz, min: 3; max: 10 */ -#define DEFAULT_SYNC_OFFSET 0 /* bytes, min: 0; max: 15; use 0 for async. mode */ - -/*************************************************************************************** -* If defined, don't allow targets to disconnect during commands. This will reduce * -* performance, but may be worthwhile if you suspect the driver of corrupting data when * -* a disconnect happens. * -***************************************************************************************/ -#define AM53C974_PROHIBIT_DISCONNECT - -/* --------------------- don't edit below here --------------------- */ - -#define AM53C974_DRIVER_REVISION_MAJOR 0 -#define AM53C974_DRIVER_REVISION_MINOR 5 -#define SEPARATOR_LINE \ -"--------------------------------------------------------------------------\n" - -/* debug control */ -/* #define AM53C974_DEBUG */ -/* #define AM53C974_DEBUG_MSG */ -/* #define AM53C974_DEBUG_KEYWAIT */ -/* #define AM53C974_DEBUG_INIT */ -/* #define AM53C974_DEBUG_QUEUE */ -/* #define AM53C974_DEBUG_INFO */ -/* #define AM53C974_DEBUG_LINKED */ -/* #define VERBOSE_AM53C974_DEBUG */ -/* #define AM53C974_DEBUG_INTR */ -/* #define AM53C974_DEB_RESEL */ -#define AM53C974_DEBUG_ABORT -/* #define AM53C974_OPTION_DEBUG_PROBE_ONLY */ - -/* special options/constants */ -#define DEF_CLK 40 /* chip clock freq. in MHz */ -#define MIN_PERIOD 4 /* for negotiation: min. number of clocks per cycle */ -#define MAX_PERIOD 13 /* for negotiation: max. number of clocks per cycle */ -#define MAX_OFFSET 15 /* for negotiation: max. offset (0=async) */ - -#define DEF_SCSI_TIMEOUT 245 /* STIMREG value, 40 Mhz */ -#define DEF_STP 8 /* STPREG value assuming 5.0 MB/sec, FASTCLK, FASTSCSI */ -#define DEF_SOF_RAD 0 /* REQ/ACK deassertion delay */ -#define DEF_SOF_RAA 0 /* REQ/ACK assertion delay */ -#define DEF_ETM 0 /* CNTLREG1, ext. timing mode */ -#define DEF_PERE 1 /* CNTLREG1, parity error reporting */ -#define DEF_CLKF 0 /* CLKFREG, 0=40 Mhz */ -#define DEF_ENF 1 /* CNTLREG2, enable features */ -#define DEF_ADIDCHK 0 /* CNTLREG3, additional ID check */ -#define DEF_FASTSCSI 1 /* CNTLREG3, fast SCSI */ -#define DEF_FASTCLK 1 /* CNTLREG3, fast clocking, 5 MB/sec at 40MHz chip clk */ -#define DEF_GLITCH 1 /* CNTLREG4, glitch eater, 0=12ns, 1=35ns, 2=25ns, 3=off */ -#define DEF_PWD 0 /* CNTLREG4, reduced power feature */ -#define DEF_RAE 0 /* CNTLREG4, RAE active negation on REQ, ACK only */ -#define DEF_RADE 1 /* 1CNTLREG4, active negation on REQ, ACK and data */ - -/*** PCI block ***/ -/* standard registers are defined in */ -#ifndef PCI_VENDOR_ID_AMD -#define PCI_VENDOR_ID_AMD 0x1022 -#define PCI_DEVICE_ID_AMD_SCSI 0x2020 -#endif -#define PCI_BASE_MASK 0xFFFFFFE0 -#define PCI_COMMAND_PERREN 0x40 -#define PCI_SCRATCH_REG_0 0x40 /* 16 bits */ -#define PCI_SCRATCH_REG_1 0x42 /* 16 bits */ -#define PCI_SCRATCH_REG_2 0x44 /* 16 bits */ -#define PCI_SCRATCH_REG_3 0x46 /* 16 bits */ -#define PCI_SCRATCH_REG_4 0x48 /* 16 bits */ -#define PCI_SCRATCH_REG_5 0x4A /* 16 bits */ -#define PCI_SCRATCH_REG_6 0x4C /* 16 bits */ -#define PCI_SCRATCH_REG_7 0x4E /* 16 bits */ - -/*** SCSI block ***/ -#define CTCLREG 0x00 /* r current transf. count, low byte */ -#define CTCMREG 0x04 /* r current transf. count, middle byte */ -#define CTCHREG 0x38 /* r current transf. count, high byte */ -#define STCLREG 0x00 /* w start transf. count, low byte */ -#define STCMREG 0x04 /* w start transf. count, middle byte */ -#define STCHREG 0x38 /* w start transf. count, high byte */ -#define FFREG 0x08 /* rw SCSI FIFO reg. */ -#define STIMREG 0x14 /* w SCSI timeout reg. */ - -#define SDIDREG 0x10 /* w SCSI destination ID reg. */ -#define SDIREG_MASK 0x07 /* mask */ - -#define STPREG 0x18 /* w synchronous transf. period reg. */ -#define STPREG_STP 0x1F /* synchr. transfer period */ - -#define CLKFREG 0x24 /* w clock factor reg. */ -#define CLKFREG_MASK 0x07 /* mask */ - -#define CMDREG 0x0C /* rw SCSI command reg. */ -#define CMDREG_DMA 0x80 /* set DMA mode (set together with opcodes below) */ -#define CMDREG_IT 0x10 /* information transfer */ -#define CMDREG_ICCS 0x11 /* initiator command complete steps */ -#define CMDREG_MA 0x12 /* message accepted */ -#define CMDREG_TPB 0x98 /* transfer pad bytes, DMA mode only */ -#define CMDREG_SATN 0x1A /* set ATN */ -#define CMDREG_RATN 0x1B /* reset ATN */ -#define CMDREG_SOAS 0x41 /* select without ATN steps */ -#define CMDREG_SAS 0x42 /* select with ATN steps (1 msg byte) */ -#define CMDREG_SASS 0x43 /* select with ATN and stop steps */ -#define CMDREG_ESR 0x44 /* enable selection/reselection */ -#define CMDREG_DSR 0x45 /* disable selection/reselection */ -#define CMDREG_SA3S 0x46 /* select with ATN 3 steps (3 msg bytes) */ -#define CMDREG_NOP 0x00 /* no operation */ -#define CMDREG_CFIFO 0x01 /* clear FIFO */ -#define CMDREG_RDEV 0x02 /* reset device */ -#define CMDREG_RBUS 0x03 /* reset SCSI bus */ - -#define STATREG 0x10 /* r SCSI status reg. */ -#define STATREG_INT 0x80 /* SCSI interrupt condition detected */ -#define STATREG_IOE 0x40 /* SCSI illegal operation error detected */ -#define STATREG_PE 0x20 /* SCSI parity error detected */ -#define STATREG_CTZ 0x10 /* CTC reg decremented to zero */ -#define STATREG_MSG 0x04 /* SCSI MSG phase (latched?) */ -#define STATREG_CD 0x02 /* SCSI C/D phase (latched?) */ -#define STATREG_IO 0x01 /* SCSI I/O phase (latched?) */ -#define STATREG_PHASE 0x07 /* SCSI phase mask */ - -#define INSTREG 0x14 /* r interrupt status reg. */ -#define INSTREG_SRST 0x80 /* SCSI reset detected */ -#define INSTREG_ICMD 0x40 /* SCSI invalid command detected */ -#define INSTREG_DIS 0x20 /* target disconnected or sel/resel timeout*/ -#define INSTREG_SR 0x10 /* device on bus has service request */ -#define INSTREG_SO 0x08 /* successful operation */ -#define INSTREG_RESEL 0x04 /* device reselected as initiator */ - -#define ISREG 0x18 /* r internal state reg. */ -#define ISREG_SOF 0x08 /* synchronous offset flag (act. low) */ -#define ISREG_IS 0x07 /* status of intermediate op. */ -#define ISREG_OK_NO_STOP 0x04 /* selection successful */ -#define ISREG_OK_STOP 0x01 /* selection successful */ - -#define CFIREG 0x1C /* r current FIFO/internal state reg. */ -#define CFIREG_IS 0xE0 /* status of intermediate op. */ -#define CFIREG_CF 0x1F /* number of bytes in SCSI FIFO */ - -#define SOFREG 0x1C /* w synchr. offset reg. */ -#define SOFREG_RAD 0xC0 /* REQ/ACK deassertion delay (sync.) */ -#define SOFREG_RAA 0x30 /* REQ/ACK assertion delay (sync.) */ -#define SOFREG_SO 0x0F /* synch. offset (sync.) */ - -#define CNTLREG1 0x20 /* rw control register one */ -#define CNTLREG1_ETM 0x80 /* set extended timing mode */ -#define CNTLREG1_DISR 0x40 /* disable interrupt on SCSI reset */ -#define CNTLREG1_PERE 0x10 /* enable parity error reporting */ -#define CNTLREG1_SID 0x07 /* host adapter SCSI ID */ - -#define CNTLREG2 0x2C /* rw control register two */ -#define CNTLREG2_ENF 0x40 /* enable features */ - -#define CNTLREG3 0x30 /* rw control register three */ -#define CNTLREG3_ADIDCHK 0x80 /* additional ID check */ -#define CNTLREG3_FASTSCSI 0x10 /* fast SCSI */ -#define CNTLREG3_FASTCLK 0x08 /* fast SCSI clocking */ - -#define CNTLREG4 0x34 /* rw control register four */ -#define CNTLREG4_GLITCH 0xC0 /* glitch eater */ -#define CNTLREG4_PWD 0x20 /* reduced power feature */ -#define CNTLREG4_RAE 0x08 /* write only, active negot. ctrl. */ -#define CNTLREG4_RADE 0x04 /* active negot. ctrl. */ -#define CNTLREG4_RES 0x10 /* reserved bit, must be 1 */ - -/*** DMA block ***/ -#define DMACMD 0x40 /* rw command */ -#define DMACMD_DIR 0x80 /* transfer direction (1=read from device) */ -#define DMACMD_INTE_D 0x40 /* DMA transfer interrupt enable */ -#define DMACMD_INTE_P 0x20 /* page transfer interrupt enable */ -#define DMACMD_MDL 0x10 /* map to memory descriptor list */ -#define DMACMD_DIAG 0x04 /* diagnostics, set to 0 */ -#define DMACMD_IDLE 0x00 /* idle cmd */ -#define DMACMD_BLAST 0x01 /* flush FIFO to memory */ -#define DMACMD_ABORT 0x02 /* terminate DMA */ -#define DMACMD_START 0x03 /* start DMA */ - -#define DMASTATUS 0x54 /* r status register */ -#define DMASTATUS_BCMPLT 0x20 /* BLAST complete */ -#define DMASTATUS_SCSIINT 0x10 /* SCSI interrupt pending */ -#define DMASTATUS_DONE 0x08 /* DMA transfer terminated */ -#define DMASTATUS_ABORT 0x04 /* DMA transfer aborted */ -#define DMASTATUS_ERROR 0x02 /* DMA transfer error */ -#define DMASTATUS_PWDN 0x02 /* power down indicator */ - -#define DMASTC 0x44 /* rw starting transfer count */ -#define DMASPA 0x48 /* rw starting physical address */ -#define DMAWBC 0x4C /* r working byte counter */ -#define DMAWAC 0x50 /* r working address counter */ -#define DMASMDLA 0x58 /* rw starting MDL address */ -#define DMAWMAC 0x5C /* r working MDL counter */ - -/*** SCSI phases ***/ -#define PHASE_MSGIN 0x07 -#define PHASE_MSGOUT 0x06 -#define PHASE_RES_1 0x05 -#define PHASE_RES_0 0x04 -#define PHASE_STATIN 0x03 -#define PHASE_CMDOUT 0x02 -#define PHASE_DATAIN 0x01 -#define PHASE_DATAOUT 0x00 - struct AM53C974_hostdata { volatile unsigned in_reset:1; /* flag, says bus reset pending */ volatile unsigned aborted:1; /* flag, says aborted */ @@ -301,122 +85,5 @@ int AM53C974_abort(Scsi_Cmnd *cmd); int AM53C974_reset (Scsi_Cmnd *cmd); -#define AM53C974_local_declare() unsigned long io_port -#define AM53C974_setio(instance) io_port = instance->io_port -#define AM53C974_read_8(addr) inb(io_port + (addr)) -#define AM53C974_write_8(addr,x) outb((x), io_port + (addr)) -#define AM53C974_read_16(addr) inw(io_port + (addr)) -#define AM53C974_write_16(addr,x) outw((x), io_port + (addr)) -#define AM53C974_read_32(addr) inl(io_port + (addr)) -#define AM53C974_write_32(addr,x) outl((x), io_port + (addr)) - -#define AM53C974_poll_int() { do { statreg = AM53C974_read_8(STATREG); } \ - while (!(statreg & STATREG_INT)) ; \ - AM53C974_read_8(INSTREG) ; } /* clear int */ -#define AM53C974_cfifo() (AM53C974_read_8(CFIREG) & CFIREG_CF) - -/* These are "special" values for the tag parameter passed to AM53C974_select. */ -#define TAG_NEXT -1 /* Use next free tag */ -#define TAG_NONE -2 /* Establish I_T_L nexus instead of I_T_L_Q - * even on SCSI-II devices */ - -/************ LILO overrides *************/ -typedef struct _override_t { - int host_scsi_id; /* SCSI id of the bus controller */ - int target_scsi_id; /* SCSI id of target */ - int max_rate; /* max. transfer rate */ - int max_offset; /* max. sync. offset, 0 = asynchronous */ - } override_t; - -/************ PCI stuff *************/ -#define AM53C974_PCIREG_OPEN() outb(0xF1, 0xCF8); outb(0, 0xCFA) -#define AM53C974_PCIREG_CLOSE() outb(0, 0xCF8) -#define AM53C974_PCIREG_READ_BYTE(instance,a) ( inb((a) + (instance)->io_port) ) -#define AM53C974_PCIREG_READ_WORD(instance,a) ( inw((a) + (instance)->io_port) ) -#define AM53C974_PCIREG_READ_DWORD(instance,a) ( inl((a) + (instance)->io_port) ) -#define AM53C974_PCIREG_WRITE_BYTE(instance,x,a) ( outb((x), (a) + (instance)->io_port) ) -#define AM53C974_PCIREG_WRITE_WORD(instance,x,a) ( outw((x), (a) + (instance)->io_port) ) -#define AM53C974_PCIREG_WRITE_DWORD(instance,x,a) ( outl((x), (a) + (instance)->io_port) ) - -typedef struct _pci_config_t { - /* start of official PCI config space header */ - union { - unsigned int device_vendor; - struct { - unsigned short vendor; - unsigned short device; - } dv; - } dv_id; -#define _device_vendor dv_id.device_vendor -#define _vendor dv_id.dv.vendor -#define _device dv_id.dv.device - union { - unsigned int status_command; - struct { - unsigned short command; - unsigned short status; - } sc; - } stat_cmd; -#define _status_command stat_cmd.status_command -#define _command stat_cmd.sc.command -#define _status stat_cmd.sc.status - union { - unsigned int class_revision; - struct { - unsigned char rev_id; - unsigned char prog_if; - unsigned char sub_class; - unsigned char base_class; - } cr; - } class_rev; -#define _class_revision class_rev.class_revision -#define _rev_id class_rev.cr.rev_id -#define _prog_if class_rev.cr.prog_if -#define _sub_class class_rev.cr.sub_class -#define _base_class class_rev.cr.base_class - union { - unsigned int bist_header_latency_cache; - struct { - unsigned char cache_line_size; - unsigned char latency_timer; - unsigned char header_type; - unsigned char bist; - } bhlc; - } bhlc; -#define _bist_header_latency_cache bhlc.bist_header_latency_cache -#define _cache_line_size bhlc.bhlc.cache_line_size -#define _latency_timer bhlc.bhlc.latency_timer -#define _header_type bhlc.bhlc.header_type -#define _bist bhlc.bhlc.bist - unsigned int _base0; - unsigned int _base1; - unsigned int _base2; - unsigned int _base3; - unsigned int _base4; - unsigned int _base5; - unsigned int rsvd1; - unsigned int rsvd2; - unsigned int _baserom; - unsigned int rsvd3; - unsigned int rsvd4; - union { - unsigned int max_min_ipin_iline; - struct { - unsigned char int_line; - unsigned char int_pin; - unsigned char min_gnt; - unsigned char max_lat; - } mmii; - } mmii; -#define _max_min_ipin_iline mmii.max_min_ipin_iline -#define _int_line mmii.mmii.int_line -#define _int_pin mmii.mmii.int_pin -#define _min_gnt mmii.mmii.min_gnt -#define _max_lat mmii.mmii.max_lat - /* end of official PCI config space header */ - unsigned short _ioaddr; /* config type 1 - private I/O addr */ - unsigned int _pcibus; /* config type 2 - private bus id */ - unsigned int _cardnum; /* config type 2 - private card number */ -} pci_config_t; - #endif /* AM53C974_H */ + diff -u --recursive --new-file v2.1.31/linux/drivers/scsi/eata_pio_proc.c linux/drivers/scsi/eata_pio_proc.c --- v2.1.31/linux/drivers/scsi/eata_pio_proc.c Thu Apr 11 23:49:41 1996 +++ linux/drivers/scsi/eata_pio_proc.c Fri Apr 4 14:03:29 1997 @@ -1,19 +1,3 @@ - -#define MAX_SCSI_DEVICE_CODE 10 -const char *const pio_scsi_dev_types[MAX_SCSI_DEVICE_CODE] = -{ - "Direct-Access ", - "Sequential-Access", - "Printer ", - "Processor ", - "WORM ", - "CD-ROM ", - "Scanner ", - "Optical Device ", - "Medium Changer ", - "Communications " -}; - /* * eata_set_info * buffer : pointer to the data that has been written to the hostfile @@ -147,4 +131,3 @@ * tab-width: 8 * End: */ - diff -u --recursive --new-file v2.1.31/linux/drivers/scsi/g_NCR5380.c linux/drivers/scsi/g_NCR5380.c --- v2.1.31/linux/drivers/scsi/g_NCR5380.c Fri Apr 4 08:52:23 1997 +++ linux/drivers/scsi/g_NCR5380.c Fri Apr 4 14:03:29 1997 @@ -331,6 +331,10 @@ int blocks = len / 128; int start = 0; int bl; +#ifdef CONFIG_SCSI_G_NCR5380_PORT + int i; +#endif + NCR5380_local_declare(); NCR5380_setup(instance); @@ -605,21 +609,6 @@ return len-start; } -const char *const private_scsi_device_types[] = -{ - "Direct-Access ", - "Sequential-Access", - "Printer ", - "Processor ", - "WORM ", - "CD-ROM ", - "Scanner ", - "Optical Device ", - "Medium Changer ", - "Communications " -}; -#define MAX_SCSI_DEVICE_CODE sizeof(private_scsi_device_types)/sizeof(char*) - int generic_NCR5380_proc_info(char* buffer, char** start, off_t offset, int length, int hostno, int inout) { int len = 0; @@ -630,7 +619,8 @@ Scsi_Device *dev; Scsi_Cmnd *ptr; struct NCR5380_hostdata *hostdata; - + extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE]; + cli(); for (scsi_ptr = first_instance; scsi_ptr; scsi_ptr=scsi_ptr->next) @@ -673,7 +663,7 @@ long tr = hostdata->time_read[dev->id] / HZ; long tw = hostdata->time_write[dev->id] / HZ; - PRINTP(" T:%d %s " ANDP dev->id ANDP (dev->type < MAX_SCSI_DEVICE_CODE) ? private_scsi_device_types[(int)dev->type] : "Unknown"); + PRINTP(" T:%d %s " ANDP dev->id ANDP (dev->type < MAX_SCSI_DEVICE_CODE) ? scsi_device_types[(int)dev->type] : "Unknown"); for (i=0; i<8; i++) if (dev->vendor[i] >= 0x20) *(buffer+(len++)) = dev->vendor[i]; diff -u --recursive --new-file v2.1.31/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c --- v2.1.31/linux/drivers/scsi/scsi.c Fri Apr 4 08:52:23 1997 +++ linux/drivers/scsi/scsi.c Fri Apr 4 14:03:29 1997 @@ -110,7 +110,6 @@ #define SCSI_BLOCK(HOST) ((HOST->block && host_active && HOST != host_active) \ || (HOST->can_queue && HOST->host_busy >= HOST->can_queue)) -#define MAX_SCSI_DEVICE_CODE 10 const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE] = { "Direct-Access ", diff -u --recursive --new-file v2.1.31/linux/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h --- v2.1.31/linux/drivers/scsi/scsi.h Fri Apr 4 08:52:23 1997 +++ linux/drivers/scsi/scsi.h Fri Apr 4 14:03:29 1997 @@ -34,6 +34,7 @@ # define FALSE 0 #endif +#define MAX_SCSI_DEVICE_CODE 10 extern void scsi_make_blocked_list(void); extern volatile int in_scan_scsis; diff -u --recursive --new-file v2.1.31/linux/drivers/scsi/scsi_proc.c linux/drivers/scsi/scsi_proc.c --- v2.1.31/linux/drivers/scsi/scsi_proc.c Sat Nov 11 03:11:19 1995 +++ linux/drivers/scsi/scsi_proc.c Fri Apr 4 14:03:29 1997 @@ -239,25 +239,12 @@ return(cmdIndex); } -#define MAX_SCSI_DEVICE_CODE 10 -const char *const scsi_dev_types[MAX_SCSI_DEVICE_CODE] = -{ - "Direct-Access ", - "Sequential-Access", - "Printer ", - "Processor ", - "WORM ", - "CD-ROM ", - "Scanner ", - "Optical Device ", - "Medium Changer ", - "Communications " -}; - void proc_print_scsidevice(Scsi_Device *scd, char *buffer, int *size, int len) { + int x, y = *size; - + extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE]; + y = sprintf(buffer + len, "Host: scsi%d Channel: %02d Id: %02d Lun: %02d\n Vendor: ", scd->host->host_no, scd->channel, scd->id, scd->lun); @@ -285,7 +272,7 @@ y += sprintf(buffer + len + y, " Type: %s ", scd->type < MAX_SCSI_DEVICE_CODE ? - scsi_dev_types[(int)scd->type] : "Unknown " ); + scsi_device_types[(int)scd->type] : "Unknown " ); y += sprintf(buffer + len + y, " ANSI" " SCSI revision: %02x", (scd->scsi_level < 3)?1:2); if (scd->scsi_level == 2) @@ -315,3 +302,4 @@ * tab-width: 8 * End: */ + diff -u --recursive --new-file v2.1.31/linux/drivers/scsi/sd.c linux/drivers/scsi/sd.c --- v2.1.31/linux/drivers/scsi/sd.c Fri Apr 4 08:52:23 1997 +++ linux/drivers/scsi/sd.c Thu Apr 3 10:23:56 1997 @@ -60,7 +60,7 @@ */ #define SD_TIMEOUT (15 * HZ) -#define SD_MOD_TIMEOUT (15 * HZ) +#define SD_MOD_TIMEOUT (75 * HZ) #define CLUSTERABLE_DEVICE(SC) (SC->host->use_clustering && \ SC->device->type != TYPE_MOD) @@ -254,6 +254,11 @@ error_sector <<= 1; if (block_sectors < 2) block_sectors = 2; } + else if (sector_size == 2048) + { + error_sector <<= 2; + if (block_sectors < 4) block_sectors = 4; + } else if (sector_size == 256) error_sector >>= 1; error_sector -= sd[MINOR(SCpnt->request.rq_dev)].start_sect; @@ -634,6 +639,13 @@ goto repeat; } + if (rscsi_disks[dev].sector_size == 2048) + if((block & 3) || (SCpnt->request.nr_sectors & 3)) { + printk("sd.c:Bad block number requested"); + SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors); + goto repeat; + } + switch (SCpnt->request.cmd) { case WRITE : @@ -900,6 +912,13 @@ cmd[1] = (SCpnt->lun << 5) & 0xe0; + if (rscsi_disks[dev].sector_size == 2048){ + if(block & 3) panic("sd.c:Bad block number requested"); + if(this_count & 3) panic("sd.c:Bad block number requested"); + block = block >> 2; + this_count = this_count >> 2; + } + if (rscsi_disks[dev].sector_size == 1024){ if(block & 1) panic("sd.c:Bad block number requested"); if(this_count & 1) panic("sd.c:Bad block number requested"); @@ -1201,6 +1220,7 @@ if (rscsi_disks[i].sector_size != 512 && rscsi_disks[i].sector_size != 1024 && + rscsi_disks[i].sector_size != 2048 && rscsi_disks[i].sector_size != 256) { printk ("sd%c : unsupported sector size %d.\n", @@ -1215,6 +1235,22 @@ return i; } } + + if( rscsi_disks[i].sector_size == 2048 ) + { + int m; + + /* + * We must fix the sd_blocksizes and sd_hardsizes + * to allow us to read the partition tables. + * The disk reading code does not allow for reading + * of partial sectors. + */ + for (m=i<<4; m<((i+1)<<4); m++) + { + sd_blocksizes[m] = 2048; + } + } { /* * The msdos fs needs to know the hardware sector size @@ -1238,6 +1274,8 @@ i+'a', hard_sector, rscsi_disks[i].capacity, mb, sz_quot, sz_rem); } + if(rscsi_disks[i].sector_size == 2048) + rscsi_disks[i].capacity <<= 2; /* Change into 512 byte sectors */ if(rscsi_disks[i].sector_size == 1024) rscsi_disks[i].capacity <<= 1; /* Change into 512 byte sectors */ if(rscsi_disks[i].sector_size == 256) @@ -1341,10 +1379,12 @@ sd_hardsizes = (int *) scsi_init_malloc((sd_template.dev_max << 4) * sizeof(int), GFP_ATOMIC); - for(i=0;i<(sd_template.dev_max << 4);i++){ - sd_blocksizes[i] = 1024; - sd_hardsizes[i] = 512; - } + for(i=0;i<(sd_template.dev_max << 4);i++) + { + sd_blocksizes[i] = 1024; + sd_hardsizes[i] = 512; + } + blksize_size[MAJOR_NR] = sd_blocksizes; hardsect_size[MAJOR_NR] = sd_hardsizes; sd = (struct hd_struct *) scsi_init_malloc((sd_template.dev_max << 4) * @@ -1477,9 +1517,15 @@ gdev->part[minor].nr_sects = 0; /* * Reset the blocksize for everything so that we can read - * the partition table. + * the partition table. Technically we will determine the + * correct block size when we revalidate, but we do this just + * to make sure that everything remains consistent. */ blksize_size[MAJOR_NR][minor] = 1024; + if( rscsi_disks[target].sector_size == 2048 ) + blksize_size[MAJOR_NR][minor] = 2048; + else + blksize_size[MAJOR_NR][minor] = 1024; } #ifdef MAYBE_REINIT diff -u --recursive --new-file v2.1.31/linux/drivers/scsi/seagate.c linux/drivers/scsi/seagate.c --- v2.1.31/linux/drivers/scsi/seagate.c Thu Mar 27 14:40:05 1997 +++ linux/drivers/scsi/seagate.c Fri Apr 4 14:03:30 1997 @@ -127,6 +127,103 @@ broken! */ #endif + +/* + Thanks to Brian Antoine for the example code in his Messy-Loss ST-01 + driver, and Mitsugu Suzuki for information on the ST-01 + SCSI host. +*/ + +/* + CONTROL defines +*/ + +#define CMD_RST 0x01 +#define CMD_SEL 0x02 +#define CMD_BSY 0x04 +#define CMD_ATTN 0x08 +#define CMD_START_ARB 0x10 +#define CMD_EN_PARITY 0x20 +#define CMD_INTR 0x40 +#define CMD_DRVR_ENABLE 0x80 + +/* + STATUS +*/ +#ifdef SWAPSTAT + #define STAT_MSG 0x08 + #define STAT_CD 0x02 +#else + #define STAT_MSG 0x02 + #define STAT_CD 0x08 +#endif + +#define STAT_BSY 0x01 +#define STAT_IO 0x04 +#define STAT_REQ 0x10 +#define STAT_SEL 0x20 +#define STAT_PARITY 0x40 +#define STAT_ARB_CMPL 0x80 + +/* + REQUESTS +*/ + +#define REQ_MASK (STAT_CD | STAT_IO | STAT_MSG) +#define REQ_DATAOUT 0 +#define REQ_DATAIN STAT_IO +#define REQ_CMDOUT STAT_CD +#define REQ_STATIN (STAT_CD | STAT_IO) +#define REQ_MSGOUT (STAT_MSG | STAT_CD) +#define REQ_MSGIN (STAT_MSG | STAT_CD | STAT_IO) + +extern volatile int seagate_st0x_timeout; + +#ifdef PARITY + #define BASE_CMD CMD_EN_PARITY +#else + #define BASE_CMD 0 +#endif + +/* + Debugging code +*/ + +#define PHASE_BUS_FREE 1 +#define PHASE_ARBITRATION 2 +#define PHASE_SELECTION 4 +#define PHASE_DATAIN 8 +#define PHASE_DATAOUT 0x10 +#define PHASE_CMDOUT 0x20 +#define PHASE_MSGIN 0x40 +#define PHASE_MSGOUT 0x80 +#define PHASE_STATUSIN 0x100 +#define PHASE_ETC (PHASE_DATAIN | PHASE_DATA_OUT | PHASE_CMDOUT | PHASE_MSGIN | PHASE_MSGOUT | PHASE_STATUSIN) +#define PRINT_COMMAND 0x200 +#define PHASE_EXIT 0x400 +#define PHASE_RESELECT 0x800 +#define DEBUG_FAST 0x1000 +#define DEBUG_SG 0x2000 +#define DEBUG_LINKED 0x4000 +#define DEBUG_BORKEN 0x8000 + +/* + * Control options - these are timeouts specified in .01 seconds. + */ + +/* 30, 20 work */ +#define ST0X_BUS_FREE_DELAY 25 +#define ST0X_SELECTION_DELAY 25 + +#define eoi() __asm__("push %%eax\nmovb $0x20, %%al\noutb %%al, $0x20\npop %%eax"::) + +#define SEAGATE 1 /* these determine the type of the controller */ +#define FD 2 + +#define ST0X_ID_STR "Seagate ST-01/ST-02" +#define FD_ID_STR "TMC-8XX/TMC-950" + + static int internal_command (unsigned char target, unsigned char lun, const void *cmnd, void *buff, int bufflen, int reselect); diff -u --recursive --new-file v2.1.31/linux/drivers/scsi/seagate.h linux/drivers/scsi/seagate.h --- v2.1.31/linux/drivers/scsi/seagate.h Fri Jan 24 09:35:51 1997 +++ linux/drivers/scsi/seagate.h Fri Apr 4 14:03:30 1997 @@ -35,102 +35,6 @@ seagate_st0x_queue_command, seagate_st0x_abort, \ seagate_st0x_reset, NULL, seagate_st0x_biosparam, \ 1, 7, SG_ALL, 1, 0, 0, DISABLE_CLUSTERING} -#endif - -/* - Thanks to Brian Antoine for the example code in his Messy-Loss ST-01 - driver, and Mitsugu Suzuki for information on the ST-01 - SCSI host. -*/ - -/* - CONTROL defines -*/ - -#define CMD_RST 0x01 -#define CMD_SEL 0x02 -#define CMD_BSY 0x04 -#define CMD_ATTN 0x08 -#define CMD_START_ARB 0x10 -#define CMD_EN_PARITY 0x20 -#define CMD_INTR 0x40 -#define CMD_DRVR_ENABLE 0x80 - -/* - STATUS -*/ -#ifdef SWAPSTAT - #define STAT_MSG 0x08 - #define STAT_CD 0x02 -#else - #define STAT_MSG 0x02 - #define STAT_CD 0x08 -#endif - -#define STAT_BSY 0x01 -#define STAT_IO 0x04 -#define STAT_REQ 0x10 -#define STAT_SEL 0x20 -#define STAT_PARITY 0x40 -#define STAT_ARB_CMPL 0x80 - -/* - REQUESTS -*/ - -#define REQ_MASK (STAT_CD | STAT_IO | STAT_MSG) -#define REQ_DATAOUT 0 -#define REQ_DATAIN STAT_IO -#define REQ_CMDOUT STAT_CD -#define REQ_STATIN (STAT_CD | STAT_IO) -#define REQ_MSGOUT (STAT_MSG | STAT_CD) -#define REQ_MSGIN (STAT_MSG | STAT_CD | STAT_IO) - -extern volatile int seagate_st0x_timeout; - -#ifdef PARITY - #define BASE_CMD CMD_EN_PARITY -#else - #define BASE_CMD 0 -#endif - -/* - Debugging code -*/ - -#define PHASE_BUS_FREE 1 -#define PHASE_ARBITRATION 2 -#define PHASE_SELECTION 4 -#define PHASE_DATAIN 8 -#define PHASE_DATAOUT 0x10 -#define PHASE_CMDOUT 0x20 -#define PHASE_MSGIN 0x40 -#define PHASE_MSGOUT 0x80 -#define PHASE_STATUSIN 0x100 -#define PHASE_ETC (PHASE_DATAIN | PHASE_DATA_OUT | PHASE_CMDOUT | PHASE_MSGIN | PHASE_MSGOUT | PHASE_STATUSIN) -#define PRINT_COMMAND 0x200 -#define PHASE_EXIT 0x400 -#define PHASE_RESELECT 0x800 -#define DEBUG_FAST 0x1000 -#define DEBUG_SG 0x2000 -#define DEBUG_LINKED 0x4000 -#define DEBUG_BORKEN 0x8000 - -/* - * Control options - these are timeouts specified in .01 seconds. - */ - -/* 30, 20 work */ -#define ST0X_BUS_FREE_DELAY 25 -#define ST0X_SELECTION_DELAY 25 - -#define eoi() __asm__("push %%eax\nmovb $0x20, %%al\noutb %%al, $0x20\npop %%eax"::) - -#define SEAGATE 1 /* these determine the type of the controller */ -#define FD 2 - -#define ST0X_ID_STR "Seagate ST-01/ST-02" -#define FD_ID_STR "TMC-8XX/TMC-950" - -#endif +#endif /* ASM */ +#endif /* _SEAGATE_H */ diff -u --recursive --new-file v2.1.31/linux/drivers/scsi/sr.c linux/drivers/scsi/sr.c --- v2.1.31/linux/drivers/scsi/sr.c Fri Apr 4 08:52:23 1997 +++ linux/drivers/scsi/sr.c Thu Apr 3 10:23:56 1997 @@ -60,9 +60,10 @@ sr_finish, sr_attach, sr_detach}; Scsi_CD * scsi_CDs = NULL; -static int * sr_sizes; +static int * sr_sizes = NULL; -static int * sr_blocksizes; +static int * sr_blocksizes = NULL; +static int * sr_hardsizes = NULL; /* Hardware sector size */ static int sr_open(struct cdrom_device_info*, int); void get_sectorsize(int); @@ -121,7 +122,8 @@ retval = scsi_ioctl(scsi_CDs[MINOR(cdi->dev)].device, SCSI_IOCTL_TEST_UNIT_READY, 0); - if(retval){ + if(retval) + { /* Unable to test, unit probably not ready. This usually * means there is no disc in the drive. Mark as changed, * and we will figure it out later once the drive is @@ -140,7 +142,18 @@ #ifdef CONFIG_BLK_DEV_SR_VENDOR sr_cd_check(cdi); #endif + + /* + * If the disk changed, the capacity will now be different, + * so we force a re-read of this information + * Force 2048 for the sector size so that filesystems won't + * be trying to use something that is too small if the disc + * has changed. + */ scsi_CDs[MINOR(cdi->dev)].needs_sector_size = 1; + + scsi_CDs[MINOR(cdi->dev)].sector_size = + sr_hardsizes[MINOR(cdi->dev)] = 2048; } return retval; } @@ -894,6 +907,12 @@ scsi_CDs[i].capacity = 0; scsi_CDs[i].needs_sector_size = 1; } + + /* + * Add this so that we have the ability to correctly gauge + * what the device is capable of. + */ + sr_hardsizes[i] = scsi_CDs[i].sector_size; scsi_CDs[i].needs_sector_size = 0; sr_sizes[i] = scsi_CDs[i].capacity >> (BLOCK_SIZE_BITS - 9); }; @@ -928,8 +947,16 @@ sr_blocksizes = (int *) scsi_init_malloc(sr_template.dev_max * sizeof(int), GFP_ATOMIC); + sr_hardsizes = (int *) scsi_init_malloc(sr_template.dev_max * + sizeof(int), GFP_ATOMIC); + + /* + * These are good guesses for the time being. + */ for(i=0;i #include static int autofs_dir_readdir(struct inode *inode, struct file *filp, diff -u --recursive --new-file v2.1.31/linux/fs/autofs/dirhash.c linux/fs/autofs/dirhash.c --- v2.1.31/linux/fs/autofs/dirhash.c Fri Apr 4 08:52:24 1997 +++ linux/fs/autofs/dirhash.c Fri Apr 4 08:34:38 1997 @@ -10,7 +10,6 @@ * * ------------------------------------------------------------------------- */ -#include #include #include #include diff -u --recursive --new-file v2.1.31/linux/fs/autofs/inode.c linux/fs/autofs/inode.c --- v2.1.31/linux/fs/autofs/inode.c Fri Apr 4 08:52:24 1997 +++ linux/fs/autofs/inode.c Fri Apr 4 08:34:38 1997 @@ -10,7 +10,6 @@ * * ------------------------------------------------------------------------- */ -#include #include #include #include @@ -143,6 +142,7 @@ sbi = (struct autofs_sb_info *) kmalloc(sizeof(struct autofs_sb_info), GFP_KERNEL); if ( !sbi ) { s->s_dev = 0; + MOD_DEC_USE_COUNT; return NULL; } DPRINTK(("autofs: starting up, sbi = %p\n",sbi)); @@ -208,7 +208,7 @@ struct statfs tmp; tmp.f_type = AUTOFS_SUPER_MAGIC; - tmp.f_bsize = PAGE_SIZE/sizeof(long); + tmp.f_bsize = 1024; tmp.f_blocks = 0; tmp.f_bfree = 0; tmp.f_bavail = 0; diff -u --recursive --new-file v2.1.31/linux/fs/autofs/root.c linux/fs/autofs/root.c --- v2.1.31/linux/fs/autofs/root.c Fri Apr 4 08:52:24 1997 +++ linux/fs/autofs/root.c Fri Apr 4 08:34:38 1997 @@ -10,7 +10,6 @@ * * ------------------------------------------------------------------------- */ -#include #include #include #include diff -u --recursive --new-file v2.1.31/linux/fs/autofs/symlink.c linux/fs/autofs/symlink.c --- v2.1.31/linux/fs/autofs/symlink.c Fri Apr 4 08:52:24 1997 +++ linux/fs/autofs/symlink.c Fri Apr 4 08:34:38 1997 @@ -10,7 +10,6 @@ * * ------------------------------------------------------------------------- */ -#include #include #include #include diff -u --recursive --new-file v2.1.31/linux/fs/autofs/waitq.c linux/fs/autofs/waitq.c --- v2.1.31/linux/fs/autofs/waitq.c Fri Apr 4 08:52:24 1997 +++ linux/fs/autofs/waitq.c Fri Apr 4 08:34:38 1997 @@ -10,8 +10,9 @@ * * ------------------------------------------------------------------------- */ -#include #include +#include +#include #include /* We make this a static variable rather than a part of the superblock; it @@ -40,6 +41,7 @@ static int autofs_write(struct file *file, const void *addr, int bytes) { unsigned short fs; + unsigned long old_signal; const char *data = (const char *)addr; int written; @@ -49,9 +51,17 @@ fs = get_fs(); set_fs(KERNEL_DS); + old_signal = current->signal; + while ( bytes && (written = file->f_op->write(file->f_inode,file,data,bytes)) > 0 ) { data += written; bytes -= written; + } + + if ( written == -EPIPE && !(old_signal & (1 << (SIGPIPE-1))) ) { + /* Keep the currently executing process from receiving a + SIGPIPE unless it was already supposed to get one */ + current->signal &= ~(1 << (SIGPIPE-1)); } set_fs(fs); diff -u --recursive --new-file v2.1.31/linux/fs/binfmt_em86.c linux/fs/binfmt_em86.c --- v2.1.31/linux/fs/binfmt_em86.c Wed Dec 31 16:00:00 1969 +++ linux/fs/binfmt_em86.c Thu Apr 3 09:56:23 1997 @@ -0,0 +1,120 @@ +/* + * linux/fs/binfmt_em86.c + * + * Based on linux/fs/binfmt_script.c + * Copyright (C) 1996 Martin von Löwis + * original #!-checking implemented by tytso. + * + * em86 changes Copyright (C) 1997 Jim Paradis + */ + +#include +#include +#include +#include +#include +#include + +#define EM86_INTERP "/usr/bin/em86" +#define EM86_I_NAME "em86" + +static int do_load_em86(struct linux_binprm *bprm,struct pt_regs *regs) +{ + char *cp, *interp, *i_name, *i_arg; + int retval; + struct elfhdr elf_ex; + + /* Make sure this is a Linux/Intel ELF executable... */ + elf_ex = *((struct elfhdr *)bprm->buf); + + if (elf_ex.e_ident[0] != 0x7f || + strncmp(&elf_ex.e_ident[1], "ELF",3) != 0) { + return -ENOEXEC; + } + + + /* First of all, some simple consistency checks */ + if ((elf_ex.e_type != ET_EXEC && + elf_ex.e_type != ET_DYN) || + (!((elf_ex.e_machine == EM_386) || (elf_ex.e_machine == EM_486))) || + (!bprm->inode->i_op || !bprm->inode->i_op->default_file_ops || + !bprm->inode->i_op->default_file_ops->mmap)){ + return -ENOEXEC; + } + + bprm->sh_bang++; /* Well, the bang-shell is implicit... */ + iput(bprm->inode); + bprm->dont_iput = 1; + + /* Unlike in the script case, we don't have to do any hairy + * parsing to find our interpreter... it's hardcoded! + */ + interp = EM86_INTERP; + i_name = EM86_I_NAME; + i_arg = NULL; /* We reserve the right to add an arg later */ + + /* + * Splice in (1) the interpreter's name for argv[0] + * (2) (optional) argument to interpreter + * (3) filename of emulated file (replace argv[0]) + * + * This is done in reverse order, because of how the + * user environment and arguments are stored. + */ + remove_arg_zero(bprm); + bprm->p = copy_strings(1, &bprm->filename, bprm->page, bprm->p, 2); + bprm->argc++; + if (i_arg) { + bprm->p = copy_strings(1, &i_arg, bprm->page, bprm->p, 2); + bprm->argc++; + } + bprm->p = copy_strings(1, &i_name, bprm->page, bprm->p, 2); + bprm->argc++; + if (!bprm->p) + return -E2BIG; + /* + * OK, now restart the process with the interpreter's inode. + * Note that we use open_namei() as the name is now in kernel + * space, and we don't need to copy it. + */ + retval = open_namei(interp, 0, 0, &bprm->inode, NULL); + if (retval) + return retval; + bprm->dont_iput=0; + retval=prepare_binprm(bprm); + if(retval<0) + return retval; + return search_binary_handler(bprm,regs); +} + +static int load_em86(struct linux_binprm *bprm,struct pt_regs *regs) +{ + int retval; + MOD_INC_USE_COUNT; + retval = do_load_em86(bprm,regs); + MOD_DEC_USE_COUNT; + return retval; +} + +struct linux_binfmt em86_format = { +#ifndef MODULE + NULL, 0, load_em86, NULL, NULL +#else + NULL, &mod_use_count_, load_em86, NULL, NULL +#endif +}; + +int init_em86_binfmt(void) { + return register_binfmt(&em86_format); +} + +#ifdef MODULE +int init_module(void) +{ + return init_em86_binfmt(); +} + +void cleanup_module( void) { + unregister_binfmt(&em86_format); +} +#endif diff -u --recursive --new-file v2.1.31/linux/fs/buffer.c linux/fs/buffer.c --- v2.1.31/linux/fs/buffer.c Thu Mar 27 14:40:06 1997 +++ linux/fs/buffer.c Fri Apr 4 08:46:15 1997 @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -498,6 +499,31 @@ } } +unsigned int get_hardblocksize(kdev_t dev) +{ + int blksize = 0; + + /* + * Get the hard sector size for the given device. If we don't know + * what it is, return 0. + */ + + if (hardsect_size[MAJOR(dev)] != NULL) + { + blksize = hardsect_size[MAJOR(dev)][MINOR(dev)]; + if (blksize != 0) + { + return blksize; + } + } + + /* + * We don't know what the hardware sector size for this device is. + * Return 0 indicating that we don't know. + */ + return 0; +} + void set_blocksize(kdev_t dev, int size) { extern int *blksize_size[]; @@ -979,17 +1005,15 @@ static inline void recover_reusable_buffer_heads(void) { if (reuse_list) { - struct buffer_head *bh; - unsigned long flags; + struct buffer_head *head; + + head = xchg(&reuse_list, NULL); - save_flags(flags); do { - cli(); - bh = reuse_list; - reuse_list = bh->b_next_free; - restore_flags(flags); + struct buffer_head *bh = head; + head = head->b_next_free; put_unused_buffer_head(bh); - } while (reuse_list); + } while (head); } } diff -u --recursive --new-file v2.1.31/linux/fs/exec.c linux/fs/exec.c --- v2.1.31/linux/fs/exec.c Sun Jan 26 02:10:10 1997 +++ linux/fs/exec.c Thu Apr 3 09:56:23 1997 @@ -78,6 +78,11 @@ #ifdef CONFIG_BINFMT_JAVA init_java_binfmt(); #endif + +#ifdef CONFIG_BINFMT_EM86 + init_em86_binfmt(); +#endif + /* This cannot be configured out of the kernel */ init_script_binfmt(); } diff -u --recursive --new-file v2.1.31/linux/fs/ext2/super.c linux/fs/ext2/super.c --- v2.1.31/linux/fs/ext2/super.c Fri Dec 27 02:03:25 1996 +++ linux/fs/ext2/super.c Thu Apr 3 10:23:56 1997 @@ -32,6 +32,7 @@ #include #include #include +#include static char error_buf[1024]; @@ -377,10 +378,26 @@ unsigned short resuid = EXT2_DEF_RESUID; unsigned short resgid = EXT2_DEF_RESGID; unsigned long logic_sb_block = 1; + unsigned long offset = 0; kdev_t dev = sb->s_dev; + int blocksize = BLOCK_SIZE; + int hblock; int db_count; int i, j; + /* + * See what the current blocksize for the device is, and + * use that as the blocksize. Otherwise (or if the blocksize + * is smaller than the default) use the default. + * This is important for devices that have a hardware + * sectorsize that is larger than the default. + */ + blocksize = get_hardblocksize(dev); + if( blocksize == 0 || blocksize < BLOCK_SIZE ) + { + blocksize = BLOCK_SIZE; + } + sb->u.ext2_sb.s_mount_opt = 0; set_opt (sb->u.ext2_sb.s_mount_opt, CHECK_NORMAL); if (!parse_options ((char *) data, &sb_block, &resuid, &resgid, @@ -391,8 +408,19 @@ MOD_INC_USE_COUNT; lock_super (sb); - set_blocksize (dev, BLOCK_SIZE); - if (!(bh = bread (dev, sb_block, BLOCK_SIZE))) { + set_blocksize (dev, blocksize); + + /* + * If the superblock doesn't start on a sector boundary, + * calculate the offset. FIXME(eric) this doesn't make sense + * that we would have to do this. + */ + if (blocksize != BLOCK_SIZE) { + logic_sb_block = (sb_block*BLOCK_SIZE) / blocksize; + offset = (sb_block*BLOCK_SIZE) % blocksize; + } + + if (!(bh = bread (dev, logic_sb_block, blocksize))) { sb->s_dev = 0; unlock_super (sb); printk ("EXT2-fs: unable to read superblock\n"); @@ -403,7 +431,7 @@ * Note: s_es must be initialized s_es as soon as possible because * some ext2 macro-instructions depend on its value */ - es = (struct ext2_super_block *) bh->b_data; + es = (struct ext2_super_block *) (((char *)bh->b_data) + offset); sb->u.ext2_sb.s_es = es; sb->s_magic = le16_to_cpu(es->s_magic); if (sb->s_magic != EXT2_SUPER_MAGIC) { @@ -438,7 +466,17 @@ if (sb->s_blocksize != BLOCK_SIZE && (sb->s_blocksize == 1024 || sb->s_blocksize == 2048 || sb->s_blocksize == 4096)) { - unsigned long offset; + /* + * Make sure the blocksize for the filesystem is larger + * than the hardware sectorsize for the machine. + */ + hblock = get_hardblocksize(dev); + if( (hblock != 0) + && (sb->s_blocksize < hblock) ) + { + printk("EXT2-fs: blocksize too small for device.\n"); + goto failed_mount; + } brelse (bh); set_blocksize (dev, sb->s_blocksize); diff -u --recursive --new-file v2.1.31/linux/fs/fat/buffer.c linux/fs/fat/buffer.c --- v2.1.31/linux/fs/fat/buffer.c Thu May 9 21:54:52 1996 +++ linux/fs/fat/buffer.c Thu Apr 3 10:23:56 1997 @@ -16,13 +16,18 @@ { struct buffer_head *ret = NULL; - /* Note that the blocksize is 512 or 1024, but the first read - is always of size 1024. Doing readahead may be counterproductive + /* Note that the blocksize is 512, 1024 or 2048, but the first read + is always of size 1024 (or 2048). Doing readahead may be counterproductive or just plain wrong. */ if (sb->s_blocksize == 512) { ret = bread (sb->s_dev,block,512); } else { - struct buffer_head *real = bread (sb->s_dev,block>>1,1024); + struct buffer_head *real; + if (sb->s_blocksize == 1024){ + real = bread (sb->s_dev,block>>1,1024); + }else{ + real = bread (sb->s_dev,block>>2,2048); + } if (real != NULL){ ret = (struct buffer_head *) @@ -59,7 +64,11 @@ */ memset (ret,0,sizeof(*ret)); ret->b_data = real->b_data; - if (block & 1) ret->b_data += 512; + if (sb->s_blocksize == 2048) { + if (block & 3) ret->b_data += (block & 3) << 9; + }else{ + if (block & 1) ret->b_data += 512; + } ret->b_next = real; }else{ brelse (real); diff -u --recursive --new-file v2.1.31/linux/fs/fat/inode.c linux/fs/fat/inode.c --- v2.1.31/linux/fs/fat/inode.c Mon Dec 30 06:45:36 1996 +++ linux/fs/fat/inode.c Thu Apr 3 10:23:56 1997 @@ -172,8 +172,8 @@ *blksize = simple_strtoul(value,&value,0); if (*value) return 0; - if (*blksize != 512 && *blksize != 1024){ - printk ("MSDOS FS: Invalid blocksize (512 or 1024)\n"); + if (*blksize != 512 && *blksize != 1024 && *blksize != 2048){ + printk ("MSDOS FS: Invalid blocksize (512, 1024 or 2048)\n"); } } else if (!strcmp(this_char,"sys_immutable")) { @@ -205,16 +205,26 @@ } } if (!parse_options((char *) data, &fat, &blksize, &debug, &opts) - || (blksize != 512 && blksize != 1024)) { + || (blksize != 512 && blksize != 1024 && blksize != 2048)) + { sb->s_dev = 0; MOD_DEC_USE_COUNT; return NULL; } cache_init(); lock_super(sb); - /* The first read is always 1024 bytes */ - sb->s_blocksize = 1024; - set_blocksize(sb->s_dev, 1024); + if( blksize > 1024 ) + { + /* Force the superblock to a larger size here. */ + sb->s_blocksize = blksize; + set_blocksize(sb->s_dev, blksize); + } + else + { + /* The first read is always 1024 bytes */ + sb->s_blocksize = 1024; + set_blocksize(sb->s_dev, 1024); + } bh = fat_bread(sb, 0); unlock_super(sb); if (bh == NULL || !fat_is_uptodate(sb,bh)) { @@ -285,7 +295,7 @@ /* the misfit with buffer cache and cluster */ /* because clusters (DOS) are often aligned */ /* on odd sectors. */ - sb->s_blocksize_bits = blksize == 512 ? 9 : 10; + sb->s_blocksize_bits = blksize == 512 ? 9 : (blksize == 1024 ? 10 : 11); if (error || debug) { /* The MSDOS_CAN_BMAP is obsolete, but left just to remember */ printk("[MS-DOS FS Rel. 12,FAT %d,check=%c,conv=%c," @@ -451,7 +461,7 @@ !is_exec(raw_entry->ext))) ? S_IRUGO|S_IWUGO : S_IRWXUGO) & ~MSDOS_SB(sb)->options.fs_umask) | S_IFREG; - inode->i_op = (sb->s_blocksize == 1024) + inode->i_op = (sb->s_blocksize == 1024 || sb->s_blocksize == 2048) ? &fat_file_inode_operations_1024 : &fat_file_inode_operations; MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start); diff -u --recursive --new-file v2.1.31/linux/fs/filesystems.c linux/fs/filesystems.c --- v2.1.31/linux/fs/filesystems.c Fri Apr 4 08:52:24 1997 +++ linux/fs/filesystems.c Fri Apr 4 12:14:09 1997 @@ -79,6 +79,10 @@ init_proc_fs(); #endif +#ifdef CONFIG_LOCKD + nlmxdr_init(); +#endif + #ifdef CONFIG_NFS_FS init_nfs_fs(); #endif diff -u --recursive --new-file v2.1.31/linux/fs/isofs/dir.c linux/fs/isofs/dir.c --- v2.1.31/linux/fs/isofs/dir.c Sun Jan 26 02:07:44 1997 +++ linux/fs/isofs/dir.c Thu Apr 3 10:23:56 1997 @@ -131,28 +131,12 @@ return 0; while (filp->f_pos < inode->i_size) { - int de_len, next_offset; + int de_len; #ifdef DEBUG printk("Block, offset, f_pos: %x %x %x\n", block, offset, filp->f_pos); printk("inode->i_size = %x\n",inode->i_size); #endif - /* Next directory_record on next CDROM sector */ - if (offset >= bufsize) { -#ifdef DEBUG - printk("offset >= bufsize\n"); -#endif - brelse(bh); - offset = 0; - block = isofs_bmap(inode, (filp->f_pos) >> bufbits); - if (!block) - return 0; - bh = breada(inode->i_dev, block, bufsize, filp->f_pos, inode->i_size); - if (!bh) - return 0; - continue; - } - de = (struct iso_directory_record *) (bh->b_data + offset); inode_number = (block << bufbits) + (offset & (bufsize - 1)); @@ -166,7 +150,7 @@ CDROM sector. If we are at the end of the directory, we kick out of the while loop. */ - if (de_len == 0) { + if ((de_len == 0) || (offset == bufsize) ) { brelse(bh); filp->f_pos = ((filp->f_pos & ~(ISOFS_BLOCK_SIZE - 1)) + ISOFS_BLOCK_SIZE); @@ -180,40 +164,18 @@ continue; } - /* Make sure that the entire directory record is in the - current bh block. - If not, put the two halves together in "tmpde" */ - next_offset = offset + de_len; - if (next_offset > bufsize) { -#ifdef DEBUG - printk("next_offset (%x) > bufsize (%x)\n",next_offset,bufsize); -#endif - next_offset &= (bufsize - 1); - memcpy(tmpde, de, bufsize - offset); - brelse(bh); - block = isofs_bmap(inode, (filp->f_pos + de_len) >> bufbits); - if (!block) - { - return 0; - } - - bh = breada(inode->i_dev, block, bufsize, - filp->f_pos, - inode->i_size); - if (!bh) - { -#ifdef DEBUG - printk("!bh block=%ld, bufsize=%ld\n",block,bufsize); - printk("filp->f_pos = %ld\n",filp->f_pos); - printk("inode->i_size = %ld\n", inode->i_size); -#endif - return 0; - } - - memcpy(bufsize - offset + (char *) tmpde, bh->b_data, next_offset); - de = tmpde; + offset += de_len; + if (offset > bufsize) { + /* + * This would only normally happen if we had + * a buggy cdrom image. All directory + * entries should terminate with a null size + * or end exactly at the end of the sector. + */ + printk("next_offset (%x) > bufsize (%x)\n", + offset,bufsize); + break; } - offset = next_offset; /* Handle the case of the '.' directory */ if (de->name_len[0] == 1 && de->name[0] == 0) { diff -u --recursive --new-file v2.1.31/linux/fs/isofs/inode.c linux/fs/isofs/inode.c --- v2.1.31/linux/fs/isofs/inode.c Fri Jan 3 01:33:26 1997 +++ linux/fs/isofs/inode.c Thu Apr 3 10:23:56 1997 @@ -86,7 +86,10 @@ popt->check = 's'; /* default: strict */ popt->conversion = 'b'; /* default: no conversion */ popt->blocksize = 1024; - popt->mode = S_IRUGO; + popt->mode = S_IRUGO | S_IXUGO; /* r-x for all. The disc could + be shared with DOS machines so + virtually anything could be + a valid executable. */ popt->gid = 0; popt->uid = 0; if (!options) return 1; @@ -227,23 +230,22 @@ struct super_block *isofs_read_super(struct super_block *s,void *data, int silent) { - struct buffer_head *bh=NULL; - int iso_blknum; - unsigned int blocksize_bits; - int high_sierra; - kdev_t dev = s->s_dev; - unsigned int vol_desc_start; - int orig_zonesize; - - struct iso_volume_descriptor *vdp; - struct hs_volume_descriptor *hdp; - - struct iso_primary_descriptor *pri = NULL; - struct hs_primary_descriptor *h_pri = NULL; + struct buffer_head * bh = NULL; + unsigned int blocksize; + unsigned int blocksize_bits; + kdev_t dev = s->s_dev; + struct hs_volume_descriptor * hdp; + struct hs_primary_descriptor * h_pri = NULL; + int high_sierra; + int iso_blknum; + struct iso9660_options opt; + int orig_zonesize; + struct iso_primary_descriptor * pri = NULL; + struct iso_directory_record * rootp; + struct iso_volume_descriptor * vdp; + unsigned int vol_desc_start; - struct iso_directory_record *rootp; - struct iso9660_options opt; MOD_INC_USE_COUNT; @@ -265,6 +267,23 @@ printk("uid = %d\n", opt.uid); #endif + /* + * First of all, get the hardware blocksize for this device. + * If we don't know what it is, or the hardware blocksize is + * larger than the blocksize the user specified, then use + * that value. + */ + blocksize = get_hardblocksize(dev); + if( (blocksize != 0) + && (blocksize > opt.blocksize) ) + { + /* + * Force the blocksize we are going to use to be the + * hardware blocksize. + */ + opt.blocksize = blocksize; + } + blocksize_bits = 0; { int i = opt.blocksize; @@ -273,6 +292,7 @@ i >>=1; } } + set_blocksize(dev, opt.blocksize); lock_super(s); @@ -362,6 +382,24 @@ /* RDE: convert log zone size to bit shift */ orig_zonesize = s -> u.isofs_sb.s_log_zone_size; + + /* + * If the zone size is smaller than the hardware sector size, + * this is a fatal error. This would occur if the + * disc drive had sectors that were 2048 bytes, but the filesystem + * had blocks that were 512 bytes (which should only very rarely + * happen. + */ + if( (blocksize != 0) + && (orig_zonesize < blocksize) ) + { + printk("Logical zone size(%ld) < hardware blocksize(%ld)\n", + orig_zonesize, blocksize); + goto out; + + } + + switch (s -> u.isofs_sb.s_log_zone_size) { case 512: s -> u.isofs_sb.s_log_zone_size = 9; break; case 1024: s -> u.isofs_sb.s_log_zone_size = 10; break; @@ -403,8 +441,20 @@ * Force the blocksize to 512 for 512 byte sectors. The file * read primitives really get it wrong in a bad way if we don't * do this. + * + * Note - we should never be setting the blocksize to something + * less than the hardware sector size for the device. If we + * do, we would end up having to read larger buffers and split + * out portions to satisfy requests. + * + * Note2- the idea here is that we want to deal with the optimal + * zonesize in the filesystem. If we have it set to something less, + * then we have horrible problems with trying to piece together + * bits of adjacent blocks in order to properly read directory + * entries. By forcing the blocksize in this way, we ensure + * that we will never be required to do this. */ - if( orig_zonesize < opt.blocksize ) + if( orig_zonesize != opt.blocksize ) { opt.blocksize = orig_zonesize; blocksize_bits = 0; @@ -482,6 +532,34 @@ printk("_isofs_bmap: block<0"); return 0; } + + /* + * If we are beyond the end of this file, don't give out any + * blocks. + */ + if( (block << ISOFS_BUFFER_BITS(inode)) >= inode->i_size ) + { + off_t max_legal_read_offset; + + /* + * If we are *way* beyond the end of the file, print a message. + * Access beyond the end of the file up to the next page boundary + * is normal, however because of the way the page cache works. + * In this case, we just return 0 so that we can properly fill + * the page with useless information without generating any + * I/O errors. + */ + max_legal_read_offset = (inode->i_size + PAGE_SIZE - 1) + & ~(PAGE_SIZE - 1); + if( (block << ISOFS_BUFFER_BITS(inode)) >= max_legal_read_offset ) + { + + printk("_isofs_bmap: block>= EOF(%d, %d)", block, + inode->i_size); + } + return 0; + } + return (inode->u.isofs_i.i_first_extent >> ISOFS_BUFFER_BITS(inode)) + block; } @@ -502,7 +580,6 @@ struct buffer_head * bh; struct iso_directory_record * raw_inode; unsigned char *pnt = NULL; - void *cpnt = NULL; int high_sierra; int block; int volume_seq_no ; @@ -519,30 +596,6 @@ raw_inode = ((struct iso_directory_record *) pnt); high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra; - if ((inode->i_ino & (bufsize - 1)) + *pnt > bufsize){ - int frag1, offset; - - offset = (inode->i_ino & (bufsize - 1)); - frag1 = bufsize - offset; - cpnt = kmalloc(*pnt,GFP_KERNEL); - if (cpnt == NULL) { - printk(KERN_INFO "NoMem ISO inode %lu\n",inode->i_ino); - brelse(bh); - goto fail; - } - memcpy(cpnt, bh->b_data + offset, frag1); - brelse(bh); - if (!(bh = bread(inode->i_dev,++block, bufsize))) { - kfree(cpnt); - printk("unable to read i-node block"); - goto fail; - } - offset += *pnt - bufsize; - memcpy((char *)cpnt+frag1, bh->b_data, offset); - pnt = ((unsigned char *) cpnt); - raw_inode = ((struct iso_directory_record *) pnt); - } - if (raw_inode->flags[-high_sierra] & 2) { inode->i_mode = S_IRUGO | S_IXUGO | S_IFDIR; inode->i_nlink = 1; /* Set to 1. We know there are 2, but @@ -682,10 +735,6 @@ else if (S_ISFIFO(inode->i_mode)) init_fifo(inode); } - if (cpnt) { - kfree (cpnt); - cpnt = NULL; - } return; fail: /* With a data error we return this information */ @@ -725,8 +774,6 @@ unsigned char bufbits = ISOFS_BUFFER_BITS(parent); unsigned int block,offset; int parent_dir, inode_number; - int old_offset; - void * cpnt = NULL; int result; int directory_size; struct buffer_head * bh; @@ -786,7 +833,7 @@ CDROM sector. If we are at the end of the directory, we kick out of the while loop. */ - if (*((unsigned char *) de) == 0) + if ((*((unsigned char *) de) == 0) || (offset == bufsize) ) { brelse(bh); offset = 0; @@ -813,31 +860,12 @@ bh block. If not, we malloc a buffer, and put the two halves together, so that we can cleanly read the block. */ - old_offset = offset; offset += *((unsigned char *) de); - if (offset >= bufsize) + if (offset > bufsize) { - unsigned int frag1; - frag1 = bufsize - old_offset; - cpnt = kmalloc(*((unsigned char *) de),GFP_KERNEL); - if (!cpnt) return -1; - memcpy(cpnt, bh->b_data + old_offset, frag1); - de = (struct iso_directory_record *) ((char *)cpnt); - brelse(bh); - offset -= bufsize; - directory_size -= bufsize; - if(directory_size < 0) - { - printk("Directory size < 0\n"); - return -1; - } - block++; - if(!(bh = bread(parent->i_dev,block,bufsize))) { - kfree(cpnt); - return -1; - } - memcpy((char *)cpnt+frag1, bh->b_data, offset); + printk("Directory overrun\n"); + goto out; } if (find_rock_ridge_relocation(de, parent) == extent){ @@ -845,20 +873,12 @@ goto out; } - if (cpnt) { - kfree(cpnt); - cpnt = NULL; - } } /* We go here for any condition we cannot handle. We also drop through to here at the end of the directory. */ out: - if (cpnt) { - kfree(cpnt); - cpnt = NULL; - } brelse(bh); #ifdef DEBUG printk("Resultant Inode %d\n",result); diff -u --recursive --new-file v2.1.31/linux/fs/isofs/rock.c linux/fs/isofs/rock.c --- v2.1.31/linux/fs/isofs/rock.c Thu Jan 2 02:11:30 1997 +++ linux/fs/isofs/rock.c Thu Apr 3 10:23:56 1997 @@ -203,6 +203,17 @@ break; case SIG('N','M'): if (truncate) break; + /* + * If the flags are 2 or 4, this indicates '.' or '..'. + * We don't want to do anything with this, because it + * screws up the code that calls us. We don't really + * care anyways, since we can just use the non-RR + * name. + */ + if (rr->u.NM.flags & 6) { + break; + } + if (rr->u.NM.flags & ~1) { printk("Unsupported NM flag settings (%d)\n",rr->u.NM.flags); break; diff -u --recursive --new-file v2.1.31/linux/fs/isofs/util.c linux/fs/isofs/util.c --- v2.1.31/linux/fs/isofs/util.c Tue May 28 21:26:48 1996 +++ linux/fs/isofs/util.c Thu Apr 3 10:23:56 1997 @@ -84,10 +84,16 @@ return (isonum_731 (p)); } -/* We have to convert from a MM/DD/YY format to the unix ctime format. We have to - take into account leap years and all of that good stuff. Unfortunately, the kernel - does not have the information on hand to take into account daylight savings time, - so there will be cases (roughly half the time) where the dates are off by one hour. */ +/* + * We have to convert from a MM/DD/YY format to the unix ctime format. + * We have to take into account leap years and all of that good stuff. + * Unfortunately, the kernel does not have the information on hand to + * take into account daylight savings time, but it shouldn't matter. + * The time stored should be localtime (with or without DST in effect), + * and the timezone offset should hold the offset required to get back + * to GMT. Thus we should always be correct. + */ + int iso_date(char * p, int flag) { int year, month, day, hour ,minute, second, tz; @@ -121,9 +127,33 @@ if (tz & 0x80) tz |= (-1 << 8); - /* timezone offset is unreliable on some disks */ - if (-48 <= tz && tz <= 52) - crtime += tz * 15 * 60; + /* + * The timezone offset is unreliable on some disks, + * so we make a sanity check. In no case is it ever + * more than 13 hours from GMT, which is 52*15min. + * The time is always stored in localtime with the + * timezone offset being what get added to GMT to + * get to localtime. Thus we need to subtract the offset + * to get to true GMT, which is what we store the time + * as internally. On the local system, the user may set + * their timezone any way they wish, of course, so GMT + * gets converted back to localtime on the receiving + * system. + * + * NOTE: mkisofs in versions prior to mkisofs-1.10 had + * the sign wrong on the timezone offset. This has now + * been corrected there too, but if you are getting screwy + * results this may be the explaination. If enough people + * complain, a user configuration option could be added + * to add the timezone offset in with the wrong sign + * for 'compatibility' with older discs, but I cannot see how + * it will matter that much. + * + * Thanks to kuhlmav@elec.canterbury.ac.nz (Volker Kuhlmann) + * for pointing out the sign error. + */ + if (-52 <= tz && tz <= 52) + crtime -= tz * 15 * 60; } return crtime; } diff -u --recursive --new-file v2.1.31/linux/fs/lockd/Makefile linux/fs/lockd/Makefile --- v2.1.31/linux/fs/lockd/Makefile Wed Dec 31 16:00:00 1969 +++ linux/fs/lockd/Makefile Fri Apr 4 11:06:05 1997 @@ -0,0 +1,16 @@ +# +# Makefile for the linux lock manager stuff +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +O_TARGET := lockd.o +O_OBJS := clntlock.o clntproc.o host.o svc.o svclock.o svcshare.o \ + svcproc.o svcsubs.o mon.o xdr.o +OX_OBJS := lockd_syms.o +M_OBJS := $(O_TARGET) + +include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.1.31/linux/fs/lockd/clntlock.c linux/fs/lockd/clntlock.c --- v2.1.31/linux/fs/lockd/clntlock.c Wed Dec 31 16:00:00 1969 +++ linux/fs/lockd/clntlock.c Fri Apr 4 11:06:05 1997 @@ -0,0 +1,203 @@ +/* + * linux/fs/lockd/clntlock.c + * + * Lock handling for the client side NLM implementation + * + * Copyright (C) 1996, Olaf Kirch + */ + +#define __KERNEL_SYSCALLS__ + +#include +#include +#include +#include +#include +#include +#include + +#define NLMDBG_FACILITY NLMDBG_CIENT + +/* + * Local function prototypes + */ +static int reclaimer(void *ptr); + +/* + * The following functions handle blocking and granting from the + * client perspective. + */ + +/* + * This is the representation of a blocked client lock. + */ +struct nlm_wait { + struct nlm_wait * b_next; /* linked list */ + struct wait_queue * b_wait; /* where to wait on */ + struct nlm_host * b_host; + struct file_lock * b_lock; /* local file lock */ + unsigned short b_reclaim; /* got to reclaim lock */ + u32 b_status; /* grant callback status */ +}; + +static struct nlm_wait * nlm_blocked = NULL; + +/* + * Block on a lock + */ +int +nlmclnt_block(struct nlm_host *host, struct file_lock *fl, u32 *statp) +{ + struct nlm_wait block, **head; + int err; + u32 pstate; + + block.b_host = host; + block.b_lock = fl; + block.b_wait = NULL; + block.b_status = NLM_LCK_BLOCKED; + block.b_next = nlm_blocked; + nlm_blocked = █ + + /* Remember pseudo nsm state */ + pstate = host->h_state; + + /* Go to sleep waiting for GRANT callback. Some servers seem + * to lose callbacks, however, so we're going to poll from + * time to time just to make sure. + * + * For now, the retry frequency is pretty high; normally + * a 1 minute timeout would do. See the comment before + * nlmclnt_lock for an explanation. + */ + current->timeout = jiffies + 30 * HZ; + interruptible_sleep_on(&block.b_wait); + + for (head = &nlm_blocked; *head; head = &(*head)->b_next) { + if (*head == &block) { + *head = block.b_next; + break; + } + } + + if (!signalled()) { + *statp = block.b_status; + return 0; + } + + /* Okay, we were interrupted. Cancel the pending request + * unless the server has rebooted. + */ + if (pstate == host->h_state && (err = nlmclnt_cancel(host, fl)) < 0) + printk(KERN_NOTICE + "lockd: CANCEL call failed (errno %d)\n", -err); + + return -ERESTARTSYS; +} + +/* + * The server lockd has called us back to tell us the lock was granted + */ +u32 +nlmclnt_grant(struct nlm_lock *lock) +{ + struct nlm_wait *block; + + /* + * Look up blocked request based on arguments. + * Warning: must not use cookie to match it! + */ + for (block = nlm_blocked; block; block = block->b_next) { + if (nlm_compare_locks(block->b_lock, &lock->fl)) + break; + } + + /* Ooops, no blocked request found. */ + if (block == NULL) + return nlm_lck_denied; + + /* Alright, we found the lock. Set the return status and + * wake up the caller. + */ + block->b_status = NLM_LCK_GRANTED; + wake_up(&block->b_wait); + + return nlm_granted; +} + +/* + * The following procedures deal with the recovery of locks after a + * server crash. + */ + +/* + * Reclaim all locks on server host. We do this by spawning a separate + * reclaimer thread. + * FIXME: should bump MOD_USE_COUNT while reclaiming + */ +void +nlmclnt_recovery(struct nlm_host *host, u32 newstate) +{ + if (!host->h_reclaiming++) { + if (host->h_nsmstate == newstate) + return; + printk(KERN_WARNING + "lockd: Uh-oh! Interfering reclaims for host %s", + host->h_name); + host->h_monitored = 0; + host->h_nsmstate = newstate; + host->h_state++; + nlm_release_host(host); + } else { + host->h_monitored = 0; + host->h_nsmstate = newstate; + host->h_state++; + host->h_count++; + kernel_thread(reclaimer, host, 0); + } +} + +static int +reclaimer(void *ptr) +{ + struct nlm_host *host = (struct nlm_host *) ptr; + struct nlm_wait *block; + struct file_lock *fl; + struct inode *inode; + + /* This one ensures that our parent doesn't terminate while the + * reclaim is in progress */ + lockd_up(); + + /* First, reclaim all locks that have been granted previously. */ + do { + for (fl = file_lock_table; fl; fl = fl->fl_next) { + inode = fl->fl_file->f_inode; + if (inode->i_sb->s_magic == NFS_SUPER_MAGIC + && nlm_cmp_addr(NFS_ADDR(inode), &host->h_addr) + && fl->fl_u.nfs_fl.state != host->h_state + && (fl->fl_u.nfs_fl.flags & NFS_LCK_GRANTED)) { + fl->fl_u.nfs_fl.flags &= ~ NFS_LCK_GRANTED; + nlmclnt_reclaim(host, fl); + break; + } + } + } while (fl); + + host->h_reclaiming = 0; + wake_up(&host->h_gracewait); + + /* Now, wake up all processes that sleep on a blocked lock */ + for (block = nlm_blocked; block; block = block->b_next) { + if (block->b_host == host) { + block->b_status = NLM_LCK_DENIED_GRACE_PERIOD; + wake_up(&block->b_wait); + } + } + + /* Release host handle after use */ + nlm_release_host(host); + lockd_down(); + + return 0; +} diff -u --recursive --new-file v2.1.31/linux/fs/lockd/clntproc.c linux/fs/lockd/clntproc.c --- v2.1.31/linux/fs/lockd/clntproc.c Wed Dec 31 16:00:00 1969 +++ linux/fs/lockd/clntproc.c Fri Apr 4 11:06:05 1997 @@ -0,0 +1,539 @@ +/* + * linux/fs/lockd/clntproc.c + * + * RPC procedures for the client side NLM implementation + * + * Copyright (C) 1996, Olaf Kirch + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NLMDBG_FACILITY NLMDBG_CLIENT + +static int nlmclnt_test(struct nlm_rqst *, struct file_lock *); +static int nlmclnt_lock(struct nlm_rqst *, struct file_lock *); +static int nlmclnt_unlock(struct nlm_rqst *, struct file_lock *); +static void nlmclnt_unlock_callback(struct rpc_task *); +static void nlmclnt_cancel_callback(struct rpc_task *); +static int nlm_stat_to_errno(u32 stat); + +/* + * Cookie counter for NLM requests + */ +static u32 nlm_cookie = 0x1234; + +/* + * Initialize arguments for TEST/LOCK/UNLOCK/CANCEL calls + */ +static inline void +nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl) +{ + struct nlm_args *argp = &req->a_args; + struct nlm_lock *lock = &argp->lock; + + memset(argp, 0, sizeof(*argp)); + argp->cookie = nlm_cookie++; + argp->state = nsm_local_state; + lock->fh = *NFS_FH(fl->fl_file->f_inode); + lock->caller = system_utsname.nodename; + lock->oh.data = req->a_owner; + lock->oh.len = sprintf(req->a_owner, "%d@%s", + current->pid, system_utsname.nodename); + lock->fl = *fl; +} + +/* + * Initialize arguments for GRANTED call + */ +int +nlmclnt_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock) +{ + struct nlm_args *argp = &call->a_args; + struct nlm_lock *alock = &argp->lock; + void *data = NULL; + + if (lock->oh.len > NLMCLNT_OHSIZE + && !(data = kmalloc(lock->oh.len, GFP_KERNEL))) + return 0; + + argp->cookie = nlm_cookie++; + argp->lock = *lock; + alock->caller = system_utsname.nodename; + if (data) + alock->oh.data = (u8 *) data; + else + alock->oh.data = call->a_owner; + memcpy(alock->oh.data, lock->oh.data, lock->oh.len); + return 1; +} + +void +nlmclnt_freegrantargs(struct nlm_rqst *call) +{ + kfree(call->a_args.lock.caller); +} + +/* + * This is the main entry point for the NLM client. + */ +int +nlmclnt_proc(struct inode *inode, int cmd, struct file_lock *fl) +{ + struct nfs_server *nfssrv = NFS_SERVER(inode); + struct nlm_host *host; + struct nlm_rqst reqst, *call = &reqst; + unsigned long oldmask; + int status; + + /* Always use NLM version 1 over UDP for now... */ + if (!(host = nlmclnt_lookup_host(NFS_ADDR(inode), IPPROTO_UDP, 1))) + return -ENOLCK; + + /* Create RPC client handle if not there, and copy soft + * and intr flags from NFS client. */ + if (host->h_rpcclnt == NULL) { + struct rpc_clnt *clnt; + + /* Bind an rpc client to this host handle (does not + * perform a portmapper lookup) */ + if (!(clnt = nlm_bind_host(host))) { + status = -ENOLCK; + goto done; + } + clnt->cl_softrtry = nfssrv->client->cl_softrtry; + clnt->cl_intr = nfssrv->client->cl_intr; + clnt->cl_chatty = nfssrv->client->cl_chatty; + } + + /* Keep the old signal mask */ + oldmask = current->blocked; + + /* If we're cleaning up locks because the process is exiting, + * perform the RPC call asynchronously. */ + if (cmd == F_SETLK && fl->fl_type == F_UNLCK + && (current->flags & PF_EXITING)) { + current->blocked = ~0UL; /* Mask all signals */ + call = nlmclnt_alloc_call(); + call->a_flags = RPC_TASK_ASYNC; + } else { + call->a_flags = 0; + } + call->a_host = host; + + /* Set up the argument struct */ + nlmclnt_setlockargs(call, fl); + + if (cmd == F_GETLK) { + status = nlmclnt_test(call, fl); + } else if (cmd == F_SETLK && fl->fl_type == F_UNLCK) { + status = nlmclnt_unlock(call, fl); + } else if (cmd == F_SETLK || cmd == F_SETLKW) { + call->a_args.block = (cmd == F_SETLKW)? 1 : 0; + status = nlmclnt_lock(call, fl); + } else { + status = -EINVAL; + } + + if (status < 0 && (call->a_flags & RPC_TASK_ASYNC)) + rpc_free(call); + + current->blocked = oldmask; + +done: + dprintk("lockd: clnt proc returns %d\n", status); + nlm_release_host(host); + return status; +} + +/* + * Wait while server is in grace period + */ +static inline int +nlmclnt_grace_wait(struct nlm_host *host) +{ + if (!host->h_reclaiming) + current->timeout = 10 * HZ; + interruptible_sleep_on(&host->h_gracewait); + return signalled()? -ERESTARTSYS : 0; +} + +/* + * Allocate an NLM RPC call struct + */ +struct nlm_rqst * +nlmclnt_alloc_call(void) +{ + struct nlm_rqst *call; + + while (!signalled()) { + call = (struct nlm_rqst *) rpc_allocate(RPC_TASK_ASYNC, + sizeof(struct nlm_rqst)); + if (call) + return call; + current->timeout = 5 * HZ; + current->state = TASK_INTERRUPTIBLE; + schedule(); + } + return NULL; +} + +/* + * Generic NLM call + */ +int +nlmclnt_call(struct nlm_rqst *req, u32 proc) +{ + struct nlm_host *host = req->a_host; + struct rpc_clnt *clnt; + struct nlm_args *argp = &req->a_args; + struct nlm_res *resp = &req->a_res; + int status; + + dprintk("lockd: call procedure %s on %s\n", + nlm_procname(proc), host->h_name); + + do { + if (host->h_reclaiming && !argp->reclaim) { + interruptible_sleep_on(&host->h_gracewait); + continue; + } + + /* If we have no RPC client yet, create one. */ + if ((clnt = nlm_bind_host(host)) == NULL) + return -ENOLCK; + + /* Perform the RPC call. If an error occurs, try again */ + if ((status = rpc_call(clnt, proc, argp, resp, 0)) < 0) { + dprintk("lockd: rpc_call returned error %d\n", -status); + if (status == -ERESTARTSYS) + return status; + nlm_rebind_host(host); + } else + if (resp->status == NLM_LCK_DENIED_GRACE_PERIOD) { + dprintk("lockd: server in grace period\n"); + if (argp->reclaim) { + printk(KERN_WARNING + "lockd: spurious grace period reject?!\n"); + return -ENOLCK; + } + } else { + dprintk("lockd: server returns status %d\n", resp->status); + return 0; /* Okay, call complete */ + } + + /* Back off a little and try again */ + current->timeout = jiffies + 15 * HZ; + interruptible_sleep_on(&host->h_gracewait); + } while (!signalled()); + + return -ERESTARTSYS; +} + +/* + * Generic NLM call, async version. + */ +int +nlmclnt_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback) +{ + struct nlm_host *host = req->a_host; + struct rpc_clnt *clnt; + struct nlm_args *argp = &req->a_args; + struct nlm_res *resp = &req->a_res; + int status; + + dprintk("lockd: call procedure %s on %s (async)\n", + nlm_procname(proc), host->h_name); + + /* If we have no RPC client yet, create one. */ + if ((clnt = nlm_bind_host(host)) == NULL) + return -ENOLCK; + + /* bootstrap and kick off the async RPC call */ + status = rpc_do_call(clnt, proc, argp, resp, RPC_TASK_ASYNC, + callback, req); + + /* If the async call is proceeding, increment host refcount */ + if (status >= 0 && (req->a_flags & RPC_TASK_ASYNC)) + host->h_count++; + return status; +} + +/* + * TEST for the presence of a conflicting lock + */ +static int +nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl) +{ + int status; + + if ((status = nlmclnt_call(req, NLMPROC_TEST)) < 0) + return status; + + status = req->a_res.status; + if (status == NLM_LCK_GRANTED) { + fl->fl_type = F_UNLCK; + } if (status == NLM_LCK_DENIED) { + /* + * Report the conflicting lock back to the application. + * FIXME: Is it OK to report the pid back as well? + */ + memcpy(fl, &req->a_res.lock.fl, sizeof(*fl)); + /* fl->fl_pid = 0; */ + } else { + return nlm_stat_to_errno(req->a_res.status); + } + + return 0; +} + +/* + * LOCK: Try to create a lock + * + * Programmer Harassment Alert + * + * When given a blocking lock request in a sync RPC call, the HPUX lockd + * will faithfully return LCK_BLOCKED but never cares to notify us when + * the lock could be granted. This way, our local process could hang + * around forever waiting for the callback. + * + * Solution A: Implement busy-waiting + * Solution B: Use the async version of the call (NLM_LOCK_{MSG,RES}) + * + * For now I am implementing solution A, because I hate the idea of + * re-implementing lockd for a third time in two months. The async + * calls shouldn't be too hard to do, however. + * + * This is one of the lovely things about standards in the NFS area: + * they're so soft and squishy you can't really blame HP for doing this. + */ +static int +nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl) +{ + struct nlm_host *host = req->a_host; + struct nlm_res *resp = &req->a_res; + int status; + + if (!host->h_monitored && nsm_monitor(host) < 0) { + printk(KERN_NOTICE "lockd: failed to monitor %s", host->h_name); + return -ENOLCK; + } + + while (1) { + if ((status = nlmclnt_call(req, NLMPROC_LOCK)) >= 0) { + if (resp->status != NLM_LCK_BLOCKED) + break; + status = nlmclnt_block(host, fl, &resp->status); + } + if (status < 0) + return status; + } + + if (resp->status == NLM_LCK_GRANTED) { + fl->fl_u.nfs_fl.state = host->h_state; + fl->fl_u.nfs_fl.flags |= NFS_LCK_GRANTED; + } + + return nlm_stat_to_errno(resp->status); +} + +/* + * RECLAIM: Try to reclaim a lock + */ +int +nlmclnt_reclaim(struct nlm_host *host, struct file_lock *fl) +{ + struct nlm_rqst reqst, *req; + int status; + + req = &reqst; + req->a_host = host; + req->a_flags = 0; + + /* Set up the argument struct */ + nlmclnt_setlockargs(req, fl); + req->a_args.reclaim = 1; + + if ((status = nlmclnt_call(req, NLMPROC_LOCK)) >= 0 + && req->a_res.status == NLM_LCK_GRANTED) + return 0; + + printk(KERN_WARNING "lockd: failed to reclaim lock for pid %d " + "(errno %d, status %d)\n", fl->fl_pid, + status, req->a_res.status); + + /* + * FIXME: This is a serious failure. We can + * + * a. Ignore the problem + * b. Send the owning process some signal (Linux doesn't have + * SIGLOST, though...) + * c. Retry the operation + * + * Until someone comes up with a simple implementation + * for b or c, I'll choose option a. + */ + + return -ENOLCK; +} + +/* + * UNLOCK: remove an existing lock + */ +static int +nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl) +{ + struct nlm_res *resp = &req->a_res; + int status; + + /* Clean the GRANTED flag now so the lock doesn't get + * reclaimed while we're stuck in the unlock call. */ + fl->fl_u.nfs_fl.flags &= ~NFS_LCK_GRANTED; + + if (req->a_flags & RPC_TASK_ASYNC) { + return nlmclnt_async_call(req, NLMPROC_UNLOCK, + nlmclnt_unlock_callback); + } + + if ((status = nlmclnt_call(req, NLMPROC_UNLOCK)) < 0) + return status; + + if (resp->status == NLM_LCK_GRANTED) + return 0; + + if (resp->status != NLM_LCK_DENIED_NOLOCKS) + printk("lockd: unexpected unlock status: %d\n", resp->status); + + /* What to do now? I'm out of my depth... */ + + return -ENOLCK; +} + +static void +nlmclnt_unlock_callback(struct rpc_task *task) +{ + struct nlm_rqst *req = (struct nlm_rqst *) task->tk_calldata; + int status = req->a_res.status; + + if (RPC_ASSASSINATED(task)) + goto die; + + if (task->tk_status < 0) { + dprintk("lockd: unlock failed (err = %d)\n", -task->tk_status); + nlm_rebind_host(req->a_host); + rpc_restart_call(task); + return; + } + if (status != NLM_LCK_GRANTED + && status != NLM_LCK_DENIED_GRACE_PERIOD) { + printk("lockd: unexpected unlock status: %d\n", status); + } + +die: + rpc_release_task(task); +} + +/* + * Cancel a blocked lock request. + * We always use an async RPC call for this in order not to hang a + * process that has been Ctrl-C'ed. + */ +int +nlmclnt_cancel(struct nlm_host *host, struct file_lock *fl) +{ + struct nlm_rqst *req; + unsigned long oldmask = current->blocked; + int status; + + /* Block all signals while setting up call */ + current->blocked = ~0UL; + + do { + req = (struct nlm_rqst *) rpc_allocate(RPC_TASK_ASYNC, + sizeof(*req)); + } while (req == NULL); + req->a_host = host; + req->a_flags = RPC_TASK_ASYNC; + + nlmclnt_setlockargs(req, fl); + + status = nlmclnt_async_call(req, NLMPROC_CANCEL, + nlmclnt_cancel_callback); + if (status < 0) + rpc_free(req); + + current->blocked = oldmask; + return status; +} + +static void +nlmclnt_cancel_callback(struct rpc_task *task) +{ + struct nlm_rqst *req = (struct nlm_rqst *) task->tk_calldata; + + if (RPC_ASSASSINATED(task)) + goto die; + + if (task->tk_status < 0) { + dprintk("lockd: CANCEL call error %d, retrying.\n", + task->tk_status); + goto retry_cancel; + } + + dprintk("lockd: cancel status %d (task %d)\n", + req->a_res.status, task->tk_pid); + + switch (req->a_res.status) { + case NLM_LCK_GRANTED: + case NLM_LCK_DENIED_GRACE_PERIOD: + /* Everything's good */ + break; + case NLM_LCK_DENIED_NOLOCKS: + dprintk("lockd: CANCEL failed (server has no locks)"); + goto retry_cancel; + default: + printk(KERN_NOTICE "lockd: weird return %d for CANCEL call", + req->a_res.status); + } + +die: + rpc_release_task(task); + nlm_release_host(req->a_host); + kfree(req); + return; + +retry_cancel: + nlm_rebind_host(req->a_host); + rpc_restart_call(task); + rpc_delay(task, 30 * HZ); + return; +} + +/* + * Convert an NLM status code to a generic kernel errno + */ +static int +nlm_stat_to_errno(u32 status) +{ + switch(status) { + case NLM_LCK_GRANTED: + return 0; + case NLM_LCK_DENIED: + return -EAGAIN; + case NLM_LCK_DENIED_NOLOCKS: + case NLM_LCK_DENIED_GRACE_PERIOD: + return -ENOLCK; + case NLM_LCK_BLOCKED: + printk(KERN_NOTICE "lockd: unexpected status NLM_BLOCKED\n"); + return -ENOLCK; + } + printk(KERN_NOTICE "lockd: unexpected server status %d\n", status); + return -ENOLCK; +} diff -u --recursive --new-file v2.1.31/linux/fs/lockd/host.c linux/fs/lockd/host.c --- v2.1.31/linux/fs/lockd/host.c Wed Dec 31 16:00:00 1969 +++ linux/fs/lockd/host.c Fri Apr 4 12:23:40 1997 @@ -0,0 +1,323 @@ +/* + * linux/fs/lockd/host.c + * + * Management for NLM peer hosts. The nlm_host struct is shared + * between client and server implementation. The only reason to + * do so is to reduce code bloat. + * + * Copyright (C) 1996, Olaf Kirch + */ + +#include +#include +#include +#include +#include +#include +#include + + +#define NLMDBG_FACILITY NLMDBG_HOSTCACHE +#define NLM_HOST_MAX 64 +#define NLM_HOST_NRHASH 32 +#define NLM_ADDRHASH(addr) (ntohl(addr) & (NLM_HOST_NRHASH-1)) +#define NLM_PTRHASH(ptr) ((((u32) ptr) / 32) & (NLM_HOST_NRHASH-1)) +#define NLM_HOST_REBIND (60 * HZ) +#define NLM_HOST_EXPIRE ((nrhosts > NLM_HOST_MAX)? 300 * HZ : 120 * HZ) +#define NLM_HOST_COLLECT ((nrhosts > NLM_HOST_MAX)? 120 * HZ : 60 * HZ) +#define NLM_HOST_ADDR(sv) (&(sv)->s_nlmclnt->cl_xprt->addr) + +static struct nlm_host * nlm_hosts[NLM_HOST_NRHASH]; +static unsigned long next_gc = 0; +static int nrhosts = 0; +static struct semaphore nlm_host_sema = MUTEX; + + +static void nlm_gc_hosts(void); + +/* + * Find an NLM server handle in the cache. If there is none, create it. + */ +struct nlm_host * +nlmclnt_lookup_host(struct sockaddr_in *sin, int proto, int version) +{ + return nlm_lookup_host(NULL, sin, proto, version); +} + +/* + * Find an NLM client handle in the cache. If there is none, create it. + */ +struct nlm_host * +nlmsvc_lookup_host(struct svc_rqst *rqstp) +{ + return nlm_lookup_host(rqstp->rq_client, &rqstp->rq_addr, 0, 0); +} + +/* + * Match the given host against client/address + */ +static inline int +nlm_match_host(struct nlm_host *host, struct svc_client *clnt, + struct sockaddr_in *sin) +{ + if (clnt) + return host->h_exportent == clnt; + return nlm_cmp_addr(&host->h_addr, sin); +} + +/* + * Common host lookup routine for server & client + */ +struct nlm_host * +nlm_lookup_host(struct svc_client *clnt, struct sockaddr_in *sin, + int proto, int version) +{ + struct nlm_host *host, **hp; + u32 addr; + int hash; + + if (!clnt && !sin) { + printk(KERN_NOTICE "lockd: no clnt or addr in lookup_host!\n"); + return NULL; + } + + dprintk("lockd: nlm_lookup_host(%08lx, p=%d, v=%d)\n", + sin? ntohl(sin->sin_addr.s_addr) : 0, proto, version); + + if (clnt) + hash = NLM_PTRHASH(clnt); + else + hash = NLM_ADDRHASH(sin->sin_addr.s_addr); + + /* Lock hash table */ + down(&nlm_host_sema); + + if (next_gc < jiffies) + nlm_gc_hosts(); + + for (hp = &nlm_hosts[hash]; (host = *hp); hp = &host->h_next) { + if (host->h_version != version || host->h_proto != proto) + continue; + + if (nlm_match_host(host, clnt, sin)) { + if (hp != nlm_hosts + hash) { + *hp = host->h_next; + host->h_next = nlm_hosts[hash]; + nlm_hosts[hash] = host; + } + host->h_expires = jiffies + NLM_HOST_EXPIRE; + host->h_count++; + up(&nlm_host_sema); + return host; + } + } + + /* special hack for nlmsvc_invalidate_client */ + if (sin == NULL) + goto nohost; + + /* Ooops, no host found, create it */ + dprintk("lockd: creating host entry\n"); + + if (!(host = (struct nlm_host *) kmalloc(sizeof(*host), GFP_KERNEL))) + goto nohost; + memset(host, 0, sizeof(*host)); + + addr = sin->sin_addr.s_addr; + sprintf(host->h_name, "%d.%d.%d.%d", + (unsigned char) (ntohl(addr) >> 24), + (unsigned char) (ntohl(addr) >> 16), + (unsigned char) (ntohl(addr) >> 8), + (unsigned char) (ntohl(addr) >> 0)); + + host->h_addr = *sin; + host->h_addr.sin_port = 0; /* ouch! */ + host->h_version = version; + host->h_proto = proto; + host->h_authflavor = RPC_AUTH_NULL; + host->h_rpcclnt = NULL; + host->h_sema = MUTEX; + host->h_nextrebind = jiffies + NLM_HOST_REBIND; + host->h_expires = jiffies + NLM_HOST_EXPIRE; + host->h_count = 1; + host->h_state = 0; /* pseudo NSM state */ + host->h_nsmstate = 0; /* real NSM state */ + host->h_exportent = clnt; + + host->h_next = nlm_hosts[hash]; + nlm_hosts[hash] = host; + + if (++nrhosts > NLM_HOST_MAX) + next_gc = 0; + +nohost: + up(&nlm_host_sema); + return host; +} + +/* + * Create the NLM RPC client for an NLM peer + */ +struct rpc_clnt * +nlm_bind_host(struct nlm_host *host) +{ + struct rpc_clnt *clnt; + struct rpc_xprt *xprt; + + dprintk("lockd: nlm_bind_host(%08lx)\n", + ntohl(host->h_addr.sin_addr.s_addr)); + + /* Lock host handle */ + down(&host->h_sema); + + /* If we've already created an RPC client, check whether + * RPC rebind is required */ + if ((clnt = host->h_rpcclnt) != NULL) { + if (host->h_nextrebind < jiffies) { + clnt->cl_port = 0; + host->h_nextrebind = jiffies + NLM_HOST_REBIND; + dprintk("lockd: next rebind in %ld jiffies\n", + host->h_nextrebind - jiffies); + } + } else { + uid_t saved_euid = current->euid; + + /* Create RPC socket as root user so we get a priv port */ + current->euid = 0; + xprt = xprt_create_proto(host->h_proto, &host->h_addr, NULL); + current->euid = saved_euid; + if (xprt == NULL) + goto forgetit; + + xprt_set_timeout(&xprt->timeout, 5, nlmsvc_timeout); + + clnt = rpc_create_client(xprt, host->h_name, &nlm_program, + host->h_version, host->h_authflavor); + if (clnt == NULL) { + xprt_destroy(xprt); + goto forgetit; + } + clnt->cl_autobind = 1; /* turn on pmap queries */ + xprt->nocong = 1; /* No congestion control for NLM */ + + host->h_rpcclnt = clnt; + } + + up(&host->h_sema); + return clnt; + +forgetit: + printk("lockd: couldn't create RPC handle for %s\n", host->h_name); + up(&host->h_sema); + return NULL; +} + +/* + * Force a portmap lookup of the remote lockd port + */ +void +nlm_rebind_host(struct nlm_host *host) +{ + dprintk("lockd: rebind host %s\n", host->h_name); + if (host->h_rpcclnt && host->h_nextrebind < jiffies) { + host->h_rpcclnt->cl_port = 0; + host->h_nextrebind = jiffies + NLM_HOST_REBIND; + } +} + +/* + * Release NLM host after use + */ +void +nlm_release_host(struct nlm_host *host) +{ + dprintk("lockd: release host %s\n", host->h_name); + host->h_count -= 1; +} + +/* + * Shut down the hosts module. + * Note that this routine is called only at server shutdown time. + */ +void +nlm_shutdown_hosts(void) +{ + struct nlm_host *host; + int i; + + dprintk("lockd: shutting down host module\n"); + down(&nlm_host_sema); + + /* First, make all hosts eligible for gc */ + dprintk("lockd: nuking all hosts...\n"); + for (i = 0; i < NLM_HOST_NRHASH; i++) { + for (host = nlm_hosts[i]; host; host = host->h_next) + host->h_expires = 0; + } + + /* Then, perform a garbage collection pass */ + nlm_gc_hosts(); + up(&nlm_host_sema); + + /* complain if any hosts are left */ + if (nrhosts) { + printk(KERN_WARNING "lockd: couldn't shutdown host module!\n"); + dprintk("lockd: %d hosts left:\n", nrhosts); + for (i = 0; i < NLM_HOST_NRHASH; i++) { + for (host = nlm_hosts[i]; host; host = host->h_next) { + dprintk(" %s (cnt %d use %d exp %ld)\n", + host->h_name, host->h_count, + host->h_inuse, host->h_expires); + } + } + } +} + +/* + * Garbage collect any unused NLM hosts. + * This GC combines reference counting for async operations with + * mark & sweep for resources held by remote clients. + */ +static void +nlm_gc_hosts(void) +{ + struct nlm_host **q, *host; + struct rpc_clnt *clnt; + int i; + + dprintk("lockd: host garbage collection\n"); + for (i = 0; i < NLM_HOST_NRHASH; i++) { + for (host = nlm_hosts[i]; host; host = host->h_next) + host->h_inuse = 0; + } + + /* Mark all hosts that hold locks, blocks or shares */ + nlmsvc_mark_resources(); + + for (i = 0; i < NLM_HOST_NRHASH; i++) { + q = &nlm_hosts[i]; + while ((host = *q) != NULL) { + if (host->h_count || host->h_inuse + || host->h_expires >= jiffies) { + q = &host->h_next; + continue; + } + dprintk("lockd: delete host %s\n", host->h_name); + *q = host->h_next; + if ((clnt = host->h_rpcclnt) != NULL) { + if (clnt->cl_users) { + printk(KERN_WARNING + "lockd: active RPC handle\n"); + clnt->cl_dead = 1; + } else { + rpc_destroy_client(host->h_rpcclnt); + } + } + kfree(host); + nrhosts--; + } + } + + next_gc = jiffies + NLM_HOST_COLLECT; +} + diff -u --recursive --new-file v2.1.31/linux/fs/lockd/lockd_syms.c linux/fs/lockd/lockd_syms.c --- v2.1.31/linux/fs/lockd/lockd_syms.c Wed Dec 31 16:00:00 1969 +++ linux/fs/lockd/lockd_syms.c Fri Apr 4 11:06:05 1997 @@ -0,0 +1,42 @@ +/* + * linux/fs/lockd/lockd_syms.c + * + * Symbols exported by the lockd module. + * + * Authors: Olaf Kirch (okir@monad.swb.de) + * + * Copyright (C) 1997 Olaf Kirch + */ + +#define __NO_VERSION__ +#include +#include + +#ifdef CONFIG_MODULES + +#include +#include +#include +#include +#include + +#include +#include +#include + +/* Start/stop the daemon */ +EXPORT_SYMBOL(lockd_up); +EXPORT_SYMBOL(lockd_down); + +/* NFS client entry */ +EXPORT_SYMBOL(nlmclnt_proc); + +/* NFS server entry points/hooks */ +EXPORT_SYMBOL(nlmsvc_invalidate_client); +EXPORT_SYMBOL(nlmsvc_ops); + +/* Configuration at insmod time */ +EXPORT_SYMBOL(nlmsvc_grace_period); +EXPORT_SYMBOL(nlmsvc_timeout); + +#endif /* CONFIG_MODULES */ diff -u --recursive --new-file v2.1.31/linux/fs/lockd/mon.c linux/fs/lockd/mon.c --- v2.1.31/linux/fs/lockd/mon.c Wed Dec 31 16:00:00 1969 +++ linux/fs/lockd/mon.c Fri Apr 4 12:21:59 1997 @@ -0,0 +1,228 @@ +/* + * linux/fs/lockd/mon.c + * + * The kernel statd client. + * + * Copyright (C) 1996, Olaf Kirch + */ + +#include +#include +#include +#include +#include +#include +#include + + +#define NLMDBG_FACILITY NLMDBG_MONITOR + +static struct rpc_clnt * nsm_create(void); + +extern struct rpc_program nsm_program; + +/* + * Local NSM state + */ +u32 nsm_local_state = 0; + +/* + * Common procedure for SM_MON/SM_UNMON calls + */ +static int +nsm_mon_unmon(struct nlm_host *host, char *what, u32 proc) +{ + struct rpc_clnt *clnt; + struct nsm_args args; + struct nsm_res res; + int status; + + dprintk("lockd: nsm_%s(%s)\n", what, host->h_name); + if (!(clnt = nsm_create())) + return -EACCES; + + args.addr = host->h_addr.sin_addr.s_addr; + args.prog = NLM_PROGRAM; + args.vers = 1; + args.proc = NLMPROC_NSM_NOTIFY; + + if ((status = rpc_call(clnt, proc, &args, &res, 0)) < 0) + return status; + + if (res.status != 0) { + printk(KERN_NOTICE "lockd: cannot %s %s\n", what, host->h_name); + return -EACCES; + } + + nsm_local_state = res.state; + return 0; +} + +/* + * Set up monitoring of a remote host + */ +int +nsm_monitor(struct nlm_host *host) +{ + int status; + + if ((status = nsm_mon_unmon(host, "monitor", SM_MON)) >= 0) + host->h_monitored = 1; + return status; +} + +/* + * Cease to monitor remote host + */ +int +nsm_unmonitor(struct nlm_host *host) +{ + int status; + + if ((status = nsm_mon_unmon(host, "unmonitor", SM_UNMON)) >= 0) + host->h_monitored = 0; + return status; +} + +/* + * Create NSM client for the local host + */ +static struct rpc_clnt * +nsm_create(void) +{ + struct sockaddr_in sin; + struct rpc_xprt *xprt; + struct rpc_clnt *clnt; + + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + sin.sin_port = 0; + + if (!(xprt = xprt_create_proto(IPPROTO_UDP, &sin, NULL))) + return NULL; + + clnt = rpc_create_client(xprt, "localhost", + &nsm_program, SM_VERSION, + RPC_AUTH_NULL); + if (!clnt) { + xprt_destroy(xprt); + } else { + clnt->cl_softrtry = 1; + clnt->cl_chatty = 1; + clnt->cl_oneshot = 1; + } + return clnt; +} + +/* + * XDR functions for NSM. + */ +static int +xdr_error(struct rpc_rqst *rqstp, u32 *p, void *dummy) +{ + return -EACCES; +} + +static int +xdr_encode_mon(struct rpc_rqst *rqstp, u32 *p, struct nsm_args *argp) +{ + char buffer[20]; + u32 addr = ntohl(argp->addr); + + dprintk("nsm: xdr_encode_mon(%08lx, %ld, %ld, %ld)\n", + htonl(argp->addr), htonl(argp->proc), + htonl(argp->vers), htonl(argp->proc)); + + /* + * Use the dotted-quad IP address of the remote host as + * identifier. Linux statd always looks up the canonical + * hostname first for whatever remote hostname it receives, + * so this works alright. + */ + sprintf(buffer, "%d.%d.%d.%d", (addr>>24) & 0xff, (addr>>16) & 0xff, + (addr>>8) & 0xff, (addr) & 0xff); + if (!(p = xdr_encode_string(p, buffer)) + || !(p = xdr_encode_string(p, system_utsname.nodename))) + return -EIO; + *p++ = htonl(argp->prog); + *p++ = htonl(argp->vers); + *p++ = htonl(argp->proc); + + /* This is the private part. Needed only for SM_MON call */ + if (rqstp->rq_task->tk_proc == SM_MON) { + *p++ = argp->addr; + *p++ = 0; + *p++ = 0; + *p++ = 0; + } + + rqstp->rq_slen = xdr_adjust_iovec(rqstp->rq_svec, p); + return 0; +} + +static int +xdr_decode_stat_res(struct rpc_rqst *rqstp, u32 *p, struct nsm_res *resp) +{ + resp->status = ntohl(*p++); + resp->state = ntohl(*p++); + dprintk("nsm: xdr_decode_stat_res status %d state %d\n", + resp->status, resp->state); + return 0; +} + +static int +xdr_decode_stat(struct rpc_rqst *rqstp, u32 *p, struct nsm_res *resp) +{ + resp->status = ntohl(*p++); + return 0; +} + +#define SM_my_name_sz (1+XDR_QUADLEN(SM_MAXSTRLEN)) +#define SM_my_id_sz (3+1+SM_my_name_sz) +#define SM_mon_id_sz (1+XDR_QUADLEN(20)+SM_my_id_sz) +#define SM_mon_sz (SM_mon_id_sz+4) + +static struct rpc_procinfo nsm_procedures[] = { + { "sm_null", + (kxdrproc_t) xdr_error, + (kxdrproc_t) xdr_error, 0, 0 }, + { "sm_stat", + (kxdrproc_t) xdr_error, + (kxdrproc_t) xdr_error, 0, 0 }, + { "sm_mon", + (kxdrproc_t) xdr_encode_mon, + (kxdrproc_t) xdr_decode_stat_res, SM_mon_sz, 2 }, + { "sm_unmon", + (kxdrproc_t) xdr_encode_mon, + (kxdrproc_t) xdr_decode_stat, SM_mon_id_sz, 1 }, + { "sm_unmon_all", + (kxdrproc_t) xdr_error, + (kxdrproc_t) xdr_error, 0, 0 }, + { "sm_simu_crash", + (kxdrproc_t) xdr_error, + (kxdrproc_t) xdr_error, 0, 0 }, + { "sm_notify", + (kxdrproc_t) xdr_error, + (kxdrproc_t) xdr_error, 0, 0 }, +}; + +static struct rpc_version nsm_version1 = { + 1, + sizeof(nsm_procedures)/sizeof(nsm_procedures[0]), + nsm_procedures +}; + +static struct rpc_version * nsm_version[] = { + NULL, + &nsm_version1, +}; + +static struct rpc_stat nsm_stats; + +struct rpc_program nsm_program = { + "statd", + SM_PROGRAM, + sizeof(nsm_version)/sizeof(nsm_version[0]), + nsm_version, + &nsm_stats +}; diff -u --recursive --new-file v2.1.31/linux/fs/lockd/svc.c linux/fs/lockd/svc.c --- v2.1.31/linux/fs/lockd/svc.c Wed Dec 31 16:00:00 1969 +++ linux/fs/lockd/svc.c Fri Apr 4 12:47:07 1997 @@ -0,0 +1,284 @@ +/* + * linux/fs/lockd/svc.c + * + * This is the central lockd service. + * + * FIXME: Separate the lockd NFS server functionality from the lockd NFS + * client functionality. Oh why didn't Sun create two separate + * services in the first place? + * + * Authors: Olaf Kirch (okir@monad.swb.de) + * + * Copyright (C) 1995, 1996 Olaf Kirch + */ + +#define __KERNEL_SYSCALLS__ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +#define NLMDBG_FACILITY NLMDBG_SVC +#define LOCKD_BUFSIZE (1024 + NLMSSVC_XDRSIZE) +#define BLOCKABLE_SIGS (~(_S(SIGKILL) | _S(SIGSTOP))) +#define _S(sig) (1 << ((sig) - 1)) + +extern struct svc_program nlmsvc_program; +struct nlmsvc_binding * nlmsvc_ops = NULL; +static int nlmsvc_sema = 0; +static int nlmsvc_pid = 0; +unsigned long nlmsvc_grace_period = 0; +unsigned long nlmsvc_timeout = 0; + +/* + * Currently the following can be set only at insmod time. + * Ideally, they would be accessible through the sysctl interface. + */ +unsigned long nlm_grace_period = 0; +unsigned long nlm_timeout = LOCKD_DFLT_TIMEO; + +/* + * This is the lockd kernel thread + */ +static void +lockd(struct svc_rqst *rqstp) +{ + struct svc_serv *serv = rqstp->rq_server; + sigset_t oldsigmask; + int err = 0; + + lock_kernel(); + /* Lock module and set up kernel thread */ + MOD_INC_USE_COUNT; + /* exit_files(current); */ + exit_mm(current); + current->session = 1; + current->pgrp = 1; + sprintf(current->comm, "lockd"); + + /* kick rpciod */ + rpciod_up(); + + dprintk("NFS locking service started (ver " LOCKD_VERSION ").\n"); + + if (!nlm_timeout) + nlm_timeout = LOCKD_DFLT_TIMEO; + +#ifdef RPC_DEBUG + nlmsvc_grace_period = 10 * HZ; +#else + if (nlm_grace_period) { + nlmsvc_grace_period += (1 + nlm_grace_period / nlm_timeout) + * nlm_timeout * HZ; + } else { + nlmsvc_grace_period += 5 * nlm_timeout * HZ; + } +#endif + + nlmsvc_grace_period += jiffies; + nlmsvc_timeout = nlm_timeout * HZ; + nlmsvc_pid = current->pid; + + /* + * The main request loop. We don't terminate until the last + * NFS mount or NFS daemon has gone away, and we've been sent a + * signal. + */ + while (nlmsvc_sema || !signalled()) { + if (signalled()) + current->signal = 0; + + /* + * Retry any blocked locks that have been notified by + * the VFS. Don't do this during grace period. + * (Theoretically, there shouldn't even be blocked locks + * during grace period). + */ + if (!nlmsvc_grace_period) { + current->timeout = nlmsvc_retry_blocked(); + } else if (nlmsvc_grace_period < jiffies) + nlmsvc_grace_period = 0; + + /* + * Find a socket with data available and call its + * recvfrom routine. + */ + if ((err = svc_recv(serv, rqstp)) == -EAGAIN) + continue; + if (err < 0) { + if (err != -EINTR) + printk(KERN_WARNING + "lockd: terminating on error %d\n", + -err); + break; + } + + dprintk("lockd: request from %08lx\n", + ntohl(rqstp->rq_addr.sin_addr.s_addr)); + + /* + * Look up the NFS client handle. The handle is needed for + * all but the GRANTED callback RPCs. + */ + if (nlmsvc_ops) { + nlmsvc_ops->exp_readlock(); + rqstp->rq_client = + nlmsvc_ops->exp_getclient(&rqstp->rq_addr); + } else { + rqstp->rq_client = NULL; + } + + /* Process request with all signals blocked. */ + oldsigmask = current->blocked; + current->blocked = BLOCKABLE_SIGS; + svc_process(serv, rqstp); + current->blocked = oldsigmask; + + /* Unlock export hash tables */ + if (nlmsvc_ops) + nlmsvc_ops->exp_unlock(); + } + + nlm_shutdown_hosts(); + + /* Exit the RPC thread */ + svc_exit_thread(rqstp); + + /* release rpciod */ + rpciod_down(); + + /* Release module */ + MOD_DEC_USE_COUNT; + nlmsvc_pid = 0; +} + +/* + * Make a socket for lockd + * FIXME: Move this to net/sunrpc/svc.c so that we can share this with nfsd. + */ +static int +lockd_makesock(struct svc_serv *serv, int protocol, unsigned short port) +{ + struct sockaddr_in sin; + + dprintk("lockd: creating socket proto = %d\n", protocol); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = INADDR_ANY; + sin.sin_port = htons(port); + return svc_create_socket(serv, protocol, &sin); +} + +int +lockd_up(void) +{ + struct svc_serv * serv; + int error; + + if (nlmsvc_pid || nlmsvc_sema++) + return 0; + + dprintk("lockd: creating service\n"); + if ((serv = svc_create(&nlmsvc_program, 0, NLMSVC_XDRSIZE)) == NULL) + return -ENOMEM; + + if ((error = lockd_makesock(serv, IPPROTO_UDP, 0)) < 0 + || (error = lockd_makesock(serv, IPPROTO_TCP, 0)) < 0) { + svc_destroy(serv); + return error; + } + + if ((error = svc_create_thread(lockd, serv)) < 0) + nlmsvc_sema--; + + /* Release server */ + svc_destroy(serv); + return 0; +} + +void +lockd_down(void) +{ + if (!nlmsvc_pid || --nlmsvc_sema > 0) + return; + + kill_proc(nlmsvc_pid, SIGKILL, 1); + nlmsvc_sema = 0; + nlmsvc_pid = 0; +} + +#ifdef MODULE +/* New module support in 2.1.18 */ +#if LINUX_VERSION_CODE >= 0x020112 + EXPORT_NO_SYMBOLS; + MODULE_AUTHOR("Olaf Kirch "); + MODULE_DESCRIPTION("NFS file locking service version " LOCKD_VERSION "."); + MODULE_PARM(nlm_grace_period, "10-240l"); + MODULE_PARM(nlm_timeout, "3-20l"); +#endif +int +init_module(void) +{ + nlmxdr_init(); + return 0; +} + +void +cleanup_module(void) +{ + /* FIXME: delete all NLM clients */ + nlm_shutdown_hosts(); +} +#endif + +/* + * Define NLM program and procedures + */ +static struct svc_version nlmsvc_version1 = { + 1, 16, nlmsvc_procedures, NULL +}; +static struct svc_version nlmsvc_version3 = { + 3, 24, nlmsvc_procedures, NULL +}; +#ifdef CONFIG_NFSD_NFS3 +static struct svc_version nlmsvc_version4 = { + 4, 24, nlmsvc_procedures4, NULL +}; +#endif +static struct svc_version * nlmsvc_version[] = { + NULL, + &nlmsvc_version1, + NULL, + &nlmsvc_version3, +#ifdef CONFIG_NFSD_NFS3 + &nlmsvc_version4, +#endif +}; + +static struct svc_stat nlmsvc_stats; + +#define NLM_NRVERS (sizeof(nlmsvc_version)/sizeof(nlmsvc_version[0])) +struct svc_program nlmsvc_program = { + NLM_PROGRAM, /* program number */ + 1, NLM_NRVERS-1, /* version range */ + NLM_NRVERS, /* number of entries in nlmsvc_version */ + nlmsvc_version, /* version table */ + "lockd", /* service name */ + &nlmsvc_stats, /* stats table */ +}; diff -u --recursive --new-file v2.1.31/linux/fs/lockd/svclock.c linux/fs/lockd/svclock.c --- v2.1.31/linux/fs/lockd/svclock.c Wed Dec 31 16:00:00 1969 +++ linux/fs/lockd/svclock.c Fri Apr 4 12:21:03 1997 @@ -0,0 +1,620 @@ +/* + * linux/fs/lockd/svclock.c + * + * Handling of server-side locks, mostly of the blocked variety. + * This is the ugliest part of lockd because we tread on very thin ice. + * GRANT and CANCEL calls may get stuck, meet in mid-flight, etc. + * IMNSHO introducing the grant callback into the NLM protocol was one + * of the worst ideas Sun ever had. Except maybe for the idea of doing + * NFS file locking at all. + * + * I'm trying hard to avoid race conditions by protecting most accesses + * to a file's list of blocked locks through a semaphore. The global + * list of blocked locks is not protected in this fashion however. + * Therefore, some functions (such as the RPC callback for the async grant + * call) move blocked locks towards the head of the list *while some other + * process might be traversing it*. This should not be a problem in + * practice, because this will only cause functions traversing the list + * to visit some blocks twice. + * + * Copyright (C) 1996, Olaf Kirch + */ + +#include +#include +#include +#include +#include +#include +#include + + +#define NLMDBG_FACILITY NLMDBG_SVCLOCK + +static void nlmsvc_insert_block(struct nlm_block *block, unsigned long); +static int nlmsvc_remove_block(struct nlm_block *block); +static void nlmsvc_grant_callback(struct rpc_task *task); +static void nlmsvc_notify_blocked(struct file_lock *); + +/* + * The list of blocked locks to retry + */ +static struct nlm_block * nlm_blocked = NULL; + +/* + * Insert a blocked lock into the global list + */ +static void +nlmsvc_insert_block(struct nlm_block *block, unsigned long when) +{ + struct nlm_block **bp, *b; + + dprintk("lockd: nlmsvc_insert_block(%p, %ld)\n", block, when); + if (block->b_queued) + nlmsvc_remove_block(block); + for (bp = &nlm_blocked; (b = *bp); bp = &b->b_next) + if (when < b->b_when) + break; + + block->b_queued = 1; + block->b_when = when; + block->b_next = b; + *bp = block; +} + +/* + * Remove a block from the global list + */ +static int +nlmsvc_remove_block(struct nlm_block *block) +{ + struct nlm_block **bp, *b; + + if (!block->b_queued) + return 1; + for (bp = &nlm_blocked; (b = *bp); bp = &b->b_next) { + if (b == block) { + *bp = block->b_next; + block->b_queued = 0; + return 1; + } + } + + return 0; +} + +/* + * Find a block for a given lock and optionally remove it from + * the list. + */ +static struct nlm_block * +nlmsvc_lookup_block(struct nlm_file *file, struct nlm_lock *lock, int remove) +{ + struct nlm_block **head, *block; + struct file_lock *fl; + + dprintk("lockd: nlmsvc_lookup_block f=%p pd=%d %ld-%ld ty=%d\n", + file, lock->fl.fl_pid, lock->fl.fl_start, + lock->fl.fl_end, lock->fl.fl_type); + for (head = &nlm_blocked; (block = *head); head = &block->b_next) { + fl = &block->b_call.a_args.lock.fl; + dprintk(" check f=%p pd=%d %ld-%ld ty=%d\n", + block->b_file, fl->fl_pid, fl->fl_start, + fl->fl_end, fl->fl_type); + if (block->b_file == file && nlm_compare_locks(fl, &lock->fl)) { + if (remove) + *head = block->b_next; + return block; + } + } + + return NULL; +} + +/* + * Find a block with a given NLM cookie. + */ +static inline struct nlm_block * +nlmsvc_find_block(u32 cookie) +{ + struct nlm_block *block; + + for (block = nlm_blocked; block; block = block->b_next) { + if (block->b_call.a_args.cookie == cookie) + break; + } + + return block; +} + +/* + * Create a block and initialize it. + * + * Note: we explicitly set the cookie of the grant reply to that of + * the blocked lock request. The spec explicitly mentions that the client + * should _not_ rely on the callback containing the same cookie as the + * request, but (as I found out later) that's because some implementations + * do just this. Never mind the standards comittees, they support our + * logging industries. + */ +static inline struct nlm_block * +nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file, + struct nlm_lock *lock, u32 cookie) +{ + struct nlm_block *block; + struct nlm_host *host; + struct nlm_rqst *call; + + /* Create host handle for callback */ + host = nlmclnt_lookup_host(&rqstp->rq_addr, + rqstp->rq_prot, rqstp->rq_vers); + if (host == NULL) + return NULL; + + /* Allocate memory for block, and initialize arguments */ + if (!(block = (struct nlm_block *) kmalloc(sizeof(*block), GFP_KERNEL))) + goto failed; + memset(block, 0, sizeof(*block)); + + /* Set notifier function for VFS, and init args */ + lock->fl.fl_notify = nlmsvc_notify_blocked; + if (!nlmclnt_setgrantargs(&block->b_call, lock)) { + kfree(block); + goto failed; + } + block->b_call.a_args.cookie = cookie; /* see above */ + + dprintk("lockd: created block %p...\n", block); + + /* Create and initialize the block */ + block->b_daemon = rqstp->rq_server; + block->b_host = host; + block->b_file = file; + + /* Add to file's list of blocks */ + block->b_fnext = file->f_blocks; + file->f_blocks = block; + + /* Set up RPC arguments for callback */ + call = &block->b_call; + call->a_host = host; + call->a_flags = RPC_TASK_ASYNC; + + return block; + +failed: + nlm_release_host(host); + return NULL; +} + +/* + * Delete a block. If the lock was cancelled or the grant callback + * failed, unlock is set to 1. + * It is the caller's responsibility to check whether the file + * can be closed hereafter. + */ +static void +nlmsvc_delete_block(struct nlm_block *block, int unlock) +{ + struct file_lock *fl = &block->b_call.a_args.lock.fl; + struct nlm_file *file = block->b_file; + struct nlm_block **bp; + + dprintk("lockd: deleting block %p...\n", block); + + /* Remove block from list */ + nlmsvc_remove_block(block); + + /* If granted, unlock it, else remove from inode block list */ + if (unlock && block->b_granted) { + dprintk("lockd: deleting granted lock\n"); + fl->fl_type = F_UNLCK; + posix_lock_file(&block->b_file->f_file, fl, 0); + block->b_granted = 0; + } else { + dprintk("lockd: unblocking blocked lock\n"); + posix_unblock_lock(fl); + } + + /* If the block is in the middle of a GRANT callback, + * don't kill it yet. */ + if (block->b_incall) { + nlmsvc_insert_block(block, NLM_NEVER); + block->b_done = 1; + return; + } + + /* Remove block from file's list of blocks */ + for (bp = &file->f_blocks; *bp; bp = &(*bp)->b_fnext) { + if (*bp == block) { + *bp = block->b_fnext; + break; + } + } + + if (block->b_host) + nlm_release_host(block->b_host); + nlmclnt_freegrantargs(&block->b_call); + kfree(block); +} + +/* + * Loop over all blocks and perform the action specified. + * (NLM_ACT_CHECK handled by nlmsvc_inspect_file). + */ +int +nlmsvc_traverse_blocks(struct nlm_host *host, struct nlm_file *file, int action) +{ + struct nlm_block *block, *next; + + down(&file->f_sema); + for (block = file->f_blocks; block; block = next) { + next = block->b_fnext; + if (action == NLM_ACT_MARK) + block->b_host->h_inuse = 1; + else if (action == NLM_ACT_UNLOCK) { + if (host == NULL || host == block->b_host) + nlmsvc_delete_block(block, 1); + } + } + up(&file->f_sema); + return 0; +} + +/* + * Attempt to establish a lock, and if it can't be granted, block it + * if required. + */ +u32 +nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, + struct nlm_lock *lock, int wait, u32 cookie) +{ + struct file_lock *conflock; + struct nlm_block *block; + int error; + + dprintk("lockd: nlmsvc_lock(%04x/%ld, ty=%d, pi=%d, %ld-%ld, bl=%d)\n", + file->f_file.f_inode->i_dev, + file->f_file.f_inode->i_ino, + lock->fl.fl_type, lock->fl.fl_pid, + lock->fl.fl_start, + lock->fl.fl_end, + wait); + + /* Lock file against concurrent access */ + down(&file->f_sema); + + /* Get existing block (in case client is busy-waiting) */ + block = nlmsvc_lookup_block(file, lock, 0); + + lock->fl.fl_flags |= FL_LOCKD; + +again: + if (!(conflock = posix_test_lock(&file->f_file, &lock->fl))) { + error = posix_lock_file(&file->f_file, &lock->fl, 0); + + if (block) + nlmsvc_delete_block(block, 0); + up(&file->f_sema); + + dprintk("lockd: posix_lock_file returned %d\n", -error); + switch(-error) { + case 0: + return nlm_granted; + case EDEADLK: /* no applicable NLM status */ + case EAGAIN: + return nlm_lck_denied; + default: /* includes ENOLCK */ + return nlm_lck_denied_nolocks; + } + } + + if (!wait) { + up(&file->f_sema); + return nlm_lck_denied; + } + + /* If we don't have a block, create and initialize it. Then + * retry because we may have slept in kmalloc. */ + if (block == NULL) { + dprintk("lockd: blocking on this lock (allocating).\n"); + if (!(block = nlmsvc_create_block(rqstp, file, lock, cookie))) + return nlm_lck_denied_nolocks; + goto again; + } + + /* Append to list of blocked */ + nlmsvc_insert_block(block, NLM_NEVER); + + /* Now add block to block list of the conflicting lock */ + dprintk("lockd: blocking on this lock.\n"); + posix_block_lock(conflock, &block->b_call.a_args.lock.fl); + + up(&file->f_sema); + return nlm_lck_blocked; +} + +/* + * Test for presence of a conflicting lock. + */ +u32 +nlmsvc_testlock(struct nlm_file *file, struct nlm_lock *lock, + struct nlm_lock *conflock) +{ + struct file_lock *fl; + + dprintk("lockd: nlmsvc_testlock(%04x/%ld, ty=%d, %ld-%ld)\n", + file->f_file.f_inode->i_dev, + file->f_file.f_inode->i_ino, + lock->fl.fl_type, + lock->fl.fl_start, + lock->fl.fl_end); + + if ((fl = posix_test_lock(&file->f_file, &lock->fl)) != NULL) { + dprintk("lockd: conflicting lock(ty=%d, %ld-%ld)\n", + fl->fl_type, fl->fl_start, fl->fl_end); + conflock->caller = "somehost"; /* FIXME */ + conflock->oh.len = 0; /* don't return OH info */ + conflock->fl = *fl; + return nlm_lck_denied; + } + + return nlm_granted; +} + +/* + * Remove a lock. + * This implies a CANCEL call: We send a GRANT_MSG, the client replies + * with a GRANT_RES call which gets lost, and calls UNLOCK immediately + * afterwards. In this case the block will still be there, and hence + * must be removed. + */ +u32 +nlmsvc_unlock(struct nlm_file *file, struct nlm_lock *lock) +{ + int error; + + dprintk("lockd: nlmsvc_unlock(%04x/%ld, pi=%d, %ld-%ld)\n", + file->f_file.f_inode->i_dev, + file->f_file.f_inode->i_ino, + lock->fl.fl_pid, + lock->fl.fl_start, + lock->fl.fl_end); + + /* First, cancel any lock that might be there */ + nlmsvc_cancel_blocked(file, lock); + + lock->fl.fl_type = F_UNLCK; + error = posix_lock_file(&file->f_file, &lock->fl, 0); + + return (error < 0)? nlm_lck_denied_nolocks : nlm_granted; +} + +/* + * Cancel a previously blocked request. + * + * A cancel request always overrides any grant that may currently + * be in progress. + * The calling procedure must check whether the file can be closed. + */ +u32 +nlmsvc_cancel_blocked(struct nlm_file *file, struct nlm_lock *lock) +{ + struct nlm_block *block; + + dprintk("lockd: nlmsvc_cancel(%04x/%ld, pi=%d, %ld-%ld)\n", + file->f_file.f_inode->i_dev, + file->f_file.f_inode->i_ino, + lock->fl.fl_pid, + lock->fl.fl_start, + lock->fl.fl_end); + + down(&file->f_sema); + if ((block = nlmsvc_lookup_block(file, lock, 1)) != NULL) + nlmsvc_delete_block(block, 1); + up(&file->f_sema); + return nlm_granted; +} + +/* + * Unblock a blocked lock request. This is a callback invoked from the + * VFS layer when a lock on which we blocked is removed. + * + * This function doesn't grant the blocked lock instantly, but rather moves + * the block to the head of nlm_blocked where it can be picked up by lockd. + */ +static void +nlmsvc_notify_blocked(struct file_lock *fl) +{ + struct nlm_block **bp, *block; + + dprintk("lockd: VFS unblock notification for block %p\n", fl); + posix_unblock_lock(fl); + for (bp = &nlm_blocked; (block = *bp); bp = &block->b_next) { + if (&block->b_call.a_args.lock.fl == fl) { + svc_wake_up(block->b_daemon); + nlmsvc_insert_block(block, 0); + return; + } + } + + printk(KERN_WARNING "lockd: notification for unknown block!\n"); +} + +/* + * Try to claim a lock that was previously blocked. + * + * Note that we use both the RPC_GRANTED_MSG call _and_ an async + * RPC thread when notifying the client. This seems like overkill... + * Here's why: + * - we don't want to use a synchronous RPC thread, otherwise + * we might find ourselves hanging on a dead portmapper. + * - Some lockd implementations (e.g. HP) don't react to + * RPC_GRANTED calls; they seem to insist on RPC_GRANTED_MSG calls. + */ +static void +nlmsvc_grant_blocked(struct nlm_block *block) +{ + struct nlm_file *file = block->b_file; + struct nlm_lock *lock = &block->b_call.a_args.lock; + struct file_lock *conflock; + int error; + + dprintk("lockd: grant blocked lock %p\n", block); + + /* First thing is lock the file */ + down(&file->f_sema); + + /* Unlink block request from list */ + nlmsvc_remove_block(block); + + /* If b_granted is true this means we've been here before. + * Just retry the grant callback, possibly refreshing the RPC + * binding */ + if (block->b_granted) { + nlm_rebind_host(block->b_host); + goto callback; + } + + /* Try the lock operation again */ + if ((conflock = posix_test_lock(&file->f_file, &lock->fl)) != NULL) { + /* Bummer, we blocked again */ + dprintk("lockd: lock still blocked\n"); + nlmsvc_insert_block(block, NLM_NEVER); + posix_block_lock(conflock, &lock->fl); + up(&file->f_sema); + return; + } + + /* Alright, no conflicting lock. Now lock it for real. If the + * following yields an error, this is most probably due to low + * memory. Retry the lock in a few seconds. + */ + if ((error = posix_lock_file(&file->f_file, &lock->fl, 0)) < 0) { + printk(KERN_WARNING "lockd: unexpected error %d in %s!\n", + -error, __FUNCTION__); + nlmsvc_insert_block(block, jiffies + 10 * HZ); + up(&file->f_sema); + return; + } + +callback: + /* Lock was granted by VFS. */ + dprintk("lockd: GRANTing blocked lock.\n"); + block->b_granted = 1; + block->b_incall = 1; + + /* Schedule next grant callback in 30 seconds */ + nlmsvc_insert_block(block, jiffies + 30 * HZ); + + /* Call the client */ + nlmclnt_async_call(&block->b_call, NLMPROC_GRANTED_MSG, + nlmsvc_grant_callback); + up(&file->f_sema); +} + +/* + * This is the callback from the RPC layer when the NLM_GRANTED_MSG + * RPC call has succeeded or timed out. + * Like all RPC callbacks, it is invoked by the rpciod process, so it + * better not sleep. Therefore, we put the blocked lock on the nlm_blocked + * chain once more in order to have it removed by lockd itself (which can + * then sleep on the file semaphore without disrupting e.g. the nfs client). + */ +static void +nlmsvc_grant_callback(struct rpc_task *task) +{ + struct nlm_rqst *call = (struct nlm_rqst *) task->tk_calldata; + struct nlm_block *block; + unsigned long timeout; + + dprintk("lockd: GRANT_MSG RPC callback\n"); + if (!(block = nlmsvc_find_block(call->a_args.cookie))) { + dprintk("lockd: no block for cookie %x\n", call->a_args.cookie); + return; + } + + /* Technically, we should down the file semaphore here. Since we + * move the block towards the head of the queue only, no harm + * can be done, though. */ + if (task->tk_status < 0) { + /* RPC error: Re-insert for retransmission */ + timeout = jiffies + 10 * HZ; + } else if (block->b_done) { + /* Block already removed, kill it for real */ + timeout = 0; + } else { + /* Call was successful, now wait for client callback */ + timeout = jiffies + 60 * HZ; + } + nlmsvc_insert_block(block, timeout); + svc_wake_up(block->b_daemon); + block->b_incall = 0; + + nlm_release_host(call->a_host); + rpc_release_task(task); +} + +/* + * We received a GRANT_RES callback. Try to find the corresponding + * block. + */ +void +nlmsvc_grant_reply(u32 cookie, u32 status) +{ + struct nlm_block *block; + struct nlm_file *file; + + if (!(block = nlmsvc_find_block(cookie))) + return; + file = block->b_file; + + file->f_count++; + down(&file->f_sema); + if ((block = nlmsvc_find_block(cookie)) != NULL) { + if (status == NLM_LCK_DENIED_GRACE_PERIOD) { + /* Try again in a couple of seconds */ + nlmsvc_insert_block(block, jiffies + 10 * HZ); + block = NULL; + } else { + /* Lock is now held by client, or has been rejected. + * In both cases, the block should be removed. */ + file->f_count++; + up(&file->f_sema); + if (status == NLM_LCK_GRANTED) + nlmsvc_delete_block(block, 0); + else + nlmsvc_delete_block(block, 1); + } + } + if (!block) + up(&file->f_sema); + nlm_release_file(file); +} + +/* + * Retry all blocked locks that have been notified. This is where lockd + * picks up locks that can be granted, or grant notifications that must + * be retransmitted. + */ +unsigned long +nlmsvc_retry_blocked(void) +{ + struct nlm_block *block; + + dprintk("nlmsvc_retry_blocked(%p, when=%ld)\n", + nlm_blocked, + nlm_blocked? nlm_blocked->b_when : 0); + while ((block = nlm_blocked) && block->b_when < jiffies) { + dprintk("nlmsvc_retry_blocked(%p, when=%ld, done=%d)\n", + block, block->b_when, block->b_done); + if (block->b_done) + nlmsvc_delete_block(block, 0); + else + nlmsvc_grant_blocked(block); + } + + if ((block = nlm_blocked) && block->b_when != NLM_NEVER) + return block->b_when; + return 0; +} diff -u --recursive --new-file v2.1.31/linux/fs/lockd/svcproc.c linux/fs/lockd/svcproc.c --- v2.1.31/linux/fs/lockd/svcproc.c Wed Dec 31 16:00:00 1969 +++ linux/fs/lockd/svcproc.c Fri Apr 4 11:06:05 1997 @@ -0,0 +1,551 @@ +/* + * linux/fs/lockd/svcproc.c + * + * Lockd server procedures. We don't implement the NLM_*_RES + * procedures because we don't use the async procedures. + * + * Copyright (C) 1996, Olaf Kirch + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define NLMDBG_FACILITY NLMDBG_CLIENT + +static u32 nlmsvc_callback(struct svc_rqst *, u32, struct nlm_res *); +static void nlmsvc_callback_exit(struct rpc_task *); + +/* + * Obtain client and file from arguments + */ +static u32 +nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp, + struct nlm_host **hostp, struct nlm_file **filp) +{ + struct nlm_host *host = NULL; + struct nlm_file *file = NULL; + struct nlm_lock *lock = &argp->lock; + u32 error; + + /* nfsd callbacks must have been installed for this procedure */ + if (!nlmsvc_ops) + return nlm_lck_denied_nolocks; + + /* Obtain handle for client host */ + if (rqstp->rq_client == NULL) { + printk(KERN_NOTICE + "lockd: unauthenticated request from (%08lx:%d)\n", + ntohl(rqstp->rq_addr.sin_addr.s_addr), + ntohs(rqstp->rq_addr.sin_port)); + return nlm_lck_denied_nolocks; + } + + /* Obtain host handle */ + if (!(host = nlmsvc_lookup_host(rqstp)) + || (argp->monitor && !host->h_monitored && nsm_monitor(host) < 0)) + goto no_locks; + *hostp = host; + + /* Obtain file pointer. Not used by FREE_ALL call. */ + if (filp != NULL) { + if ((error = nlm_lookup_file(rqstp, &file, &lock->fh)) != 0) + goto no_locks; + *filp = file; + + /* Set up the missing parts of the file_lock structure */ + lock->fl.fl_file = &file->f_file; + lock->fl.fl_owner = host; + } + + return 0; + +no_locks: + if (host) + nlm_release_host(host); + return nlm_lck_denied_nolocks; +} + +/* + * NULL: Test for presence of service + */ +static int +nlmsvc_proc_null(struct svc_rqst *rqstp, void *argp, void *resp) +{ + dprintk("lockd: NULL called\n"); + return rpc_success; +} + +/* + * TEST: Check for conflicting lock + */ +static int +nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp, + struct nlm_res *resp) +{ + struct nlm_host *host; + struct nlm_file *file; + + dprintk("lockd: TEST called\n"); + resp->cookie = argp->cookie; + + /* Don't accept test requests during grace period */ + if (nlmsvc_grace_period) { + resp->status = nlm_lck_denied_grace_period; + return rpc_success; + } + + /* Obtain client and file */ + if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) + return rpc_success; + + /* Now check for conflicting locks */ + resp->status = nlmsvc_testlock(file, &argp->lock, &resp->lock); + + dprintk("lockd: TEST status %ld\n", ntohl(resp->status)); + nlm_release_host(host); + nlm_release_file(file); + return rpc_success; +} + +static int +nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp, + struct nlm_res *resp) +{ + struct nlm_host *host; + struct nlm_file *file; + + dprintk("lockd: LOCK called\n"); + + resp->cookie = argp->cookie; + + /* Don't accept new lock requests during grace period */ + if (nlmsvc_grace_period && !argp->reclaim) { + resp->status = nlm_lck_denied_grace_period; + return rpc_success; + } + + /* Obtain client and file */ + if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) + return rpc_success; + +#if 0 + /* If supplied state doesn't match current state, we assume it's + * an old request that time-warped somehow. Any error return would + * do in this case because it's irrelevant anyway. + * + * NB: We don't retrieve the remote host's state yet. + */ + if (host->h_nsmstate && host->h_nsmstate != argp->state) { + resp->status = nlm_lck_denied_nolocks; + } else +#endif + + /* Now try to lock the file */ + resp->status = nlmsvc_lock(rqstp, file, &argp->lock, + argp->block, argp->cookie); + + dprintk("lockd: LOCK status %ld\n", ntohl(resp->status)); + nlm_release_host(host); + nlm_release_file(file); + return rpc_success; +} + +static int +nlmsvc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp, + struct nlm_res *resp) +{ + struct nlm_host *host; + struct nlm_file *file; + + dprintk("lockd: CANCEL called\n"); + + resp->cookie = argp->cookie; + + /* Don't accept requests during grace period */ + if (nlmsvc_grace_period) { + resp->status = nlm_lck_denied_grace_period; + return rpc_success; + } + + /* Obtain client and file */ + if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) + return rpc_success; + + /* Try to cancel request. */ + resp->status = nlmsvc_cancel_blocked(file, &argp->lock); + + dprintk("lockd: CANCEL status %ld\n", ntohl(resp->status)); + nlm_release_host(host); + nlm_release_file(file); + return rpc_success; +} + +/* + * UNLOCK: release a lock + */ +static int +nlmsvc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp, + struct nlm_res *resp) +{ + struct nlm_host *host; + struct nlm_file *file; + + dprintk("lockd: UNLOCK called\n"); + + resp->cookie = argp->cookie; + + /* Don't accept new lock requests during grace period */ + if (nlmsvc_grace_period) { + resp->status = nlm_lck_denied_grace_period; + return rpc_success; + } + + /* Obtain client and file */ + if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) + return rpc_success; + + /* Now try to remove the lock */ + resp->status = nlmsvc_unlock(file, &argp->lock); + + dprintk("lockd: UNLOCK status %ld\n", ntohl(resp->status)); + nlm_release_host(host); + nlm_release_file(file); + return rpc_success; +} + +/* + * GRANTED: A server calls us to tell that a process' lock request + * was granted + */ +static int +nlmsvc_proc_granted(struct svc_rqst *rqstp, struct nlm_args *argp, + struct nlm_res *resp) +{ + resp->cookie = argp->cookie; + + dprintk("lockd: GRANTED called\n"); + resp->status = nlmclnt_grant(&argp->lock); + dprintk("lockd: GRANTED status %ld\n", ntohl(resp->status)); + return rpc_success; +} + +/* + * `Async' versions of the above service routines. They aren't really, + * because we send the callback before the reply proper. I hope this + * doesn't break any clients. + */ +static int +nlmsvc_proc_test_msg(struct svc_rqst *rqstp, struct nlm_args *argp, + void *resp) +{ + struct nlm_res res; + u32 stat; + + dprintk("lockd: TEST_MSG called\n"); + + if ((stat = nlmsvc_proc_test(rqstp, argp, &res)) == 0) + stat = nlmsvc_callback(rqstp, NLMPROC_TEST_RES, &res); + return stat; +} + +static int +nlmsvc_proc_lock_msg(struct svc_rqst *rqstp, struct nlm_args *argp, + void *resp) +{ + struct nlm_res res; + u32 stat; + + dprintk("lockd: LOCK_MSG called\n"); + + if ((stat = nlmsvc_proc_lock(rqstp, argp, &res)) == 0) + stat = nlmsvc_callback(rqstp, NLMPROC_LOCK_RES, &res); + return stat; +} + +static int +nlmsvc_proc_cancel_msg(struct svc_rqst *rqstp, struct nlm_args *argp, + void *resp) +{ + struct nlm_res res; + u32 stat; + + dprintk("lockd: CANCEL_MSG called\n"); + + if ((stat = nlmsvc_proc_cancel(rqstp, argp, &res)) == 0) + stat = nlmsvc_callback(rqstp, NLMPROC_CANCEL_RES, &res); + return stat; +} + +static int +nlmsvc_proc_unlock_msg(struct svc_rqst *rqstp, struct nlm_args *argp, + void *resp) +{ + struct nlm_res res; + u32 stat; + + dprintk("lockd: UNLOCK_MSG called\n"); + + if ((stat = nlmsvc_proc_unlock(rqstp, argp, &res)) == 0) + stat = nlmsvc_callback(rqstp, NLMPROC_UNLOCK_RES, &res); + return stat; +} + +static int +nlmsvc_proc_granted_msg(struct svc_rqst *rqstp, struct nlm_args *argp, + void *resp) +{ + struct nlm_res res; + u32 stat; + + dprintk("lockd: GRANTED_MSG called\n"); + + if ((stat = nlmsvc_proc_granted(rqstp, argp, &res)) == 0) + stat = nlmsvc_callback(rqstp, NLMPROC_GRANTED_RES, &res); + return stat; +} + +/* + * SHARE: create a DOS share or alter existing share. + */ +static int +nlmsvc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp, + struct nlm_res *resp) +{ + struct nlm_host *host; + struct nlm_file *file; + + dprintk("lockd: SHARE called\n"); + + resp->cookie = argp->cookie; + + /* Don't accept new lock requests during grace period */ + if (nlmsvc_grace_period && !argp->reclaim) { + resp->status = nlm_lck_denied_grace_period; + return rpc_success; + } + + /* Obtain client and file */ + if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) + return rpc_success; + + /* Now try to create the share */ + resp->status = nlmsvc_share_file(host, file, argp); + + dprintk("lockd: SHARE status %ld\n", ntohl(resp->status)); + nlm_release_host(host); + nlm_release_file(file); + return rpc_success; +} + +/* + * UNSHARE: Release a DOS share. + */ +static int +nlmsvc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp, + struct nlm_res *resp) +{ + struct nlm_host *host; + struct nlm_file *file; + + dprintk("lockd: UNSHARE called\n"); + + resp->cookie = argp->cookie; + + /* Don't accept requests during grace period */ + if (nlmsvc_grace_period) { + resp->status = nlm_lck_denied_grace_period; + return rpc_success; + } + + /* Obtain client and file */ + if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) + return rpc_success; + + /* Now try to lock the file */ + resp->status = nlmsvc_unshare_file(host, file, argp); + + dprintk("lockd: UNSHARE status %ld\n", ntohl(resp->status)); + nlm_release_host(host); + nlm_release_file(file); + return rpc_success; +} + +/* + * NM_LOCK: Create an unmonitored lock + */ +static int +nlmsvc_proc_nm_lock(struct svc_rqst *rqstp, struct nlm_args *argp, + struct nlm_res *resp) +{ + dprintk("lockd: NM_LOCK called\n"); + + argp->monitor = 0; /* just clean the monitor flag */ + return nlmsvc_proc_lock(rqstp, argp, resp); +} + +/* + * FREE_ALL: Release all locks and shares held by client + */ +static int +nlmsvc_proc_free_all(struct svc_rqst *rqstp, struct nlm_args *argp, + void *resp) +{ + struct nlm_host *host; + + /* Obtain client */ + if (nlmsvc_retrieve_args(rqstp, argp, &host, NULL)) + return rpc_success; + + nlmsvc_free_host_resources(host); + nlm_release_host(host); + return rpc_success; +} + +/* + * SM_NOTIFY: private callback from statd (not part of official NLM proto) + */ +static int +nlmsvc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp, + void *resp) +{ + struct sockaddr_in saddr = rqstp->rq_addr; + struct nlm_host *host; + + dprintk("lockd: SM_NOTIFY called\n"); + if (saddr.sin_addr.s_addr != htonl(INADDR_LOOPBACK) + || ntohs(saddr.sin_port) >= 1024) { + printk(KERN_WARNING + "lockd: rejected NSM callback from %08lx:%d\n", + ntohl(rqstp->rq_addr.sin_addr.s_addr), + ntohs(rqstp->rq_addr.sin_port)); + return rpc_system_err; + } + + /* Obtain the host pointer for this NFS server and try to + * reclaim all locks we hold on this server. + */ + saddr.sin_addr.s_addr = argp->addr; + if ((host = nlm_lookup_host(NULL, &saddr, IPPROTO_UDP, 1)) != NULL) { + nlmclnt_recovery(host, argp->state); + nlm_release_host(host); + } + + /* If we run on an NFS server, delete all locks held by the client */ + if (nlmsvc_ops != NULL) { + struct svc_client *clnt; + saddr.sin_addr.s_addr = argp->addr; + if ((clnt = nlmsvc_ops->exp_getclient(&saddr)) != NULL + && (host = nlm_lookup_host(clnt, &saddr, 0, 0)) != NULL) { + nlmsvc_free_host_resources(host); + } + nlm_release_host(host); + } + + return rpc_success; +} + +/* + * This is the generic lockd callback for async RPC calls + */ +static u32 +nlmsvc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_res *resp) +{ + struct nlm_host *host; + struct nlm_rqst *call; + + if (!(call = nlmclnt_alloc_call())) + return rpc_system_err; + + host = nlmclnt_lookup_host(&rqstp->rq_addr, + rqstp->rq_prot, rqstp->rq_vers); + if (!host) { + kfree(call); + return rpc_system_err; + } + + call->a_flags = RPC_TASK_ASYNC; + call->a_host = host; + memcpy(&call->a_args, resp, sizeof(*resp)); + + if (nlmclnt_async_call(call, proc, nlmsvc_callback_exit) < 0) + return rpc_system_err; + + return rpc_success; +} + +static void +nlmsvc_callback_exit(struct rpc_task *task) +{ + struct nlm_rqst *call = (struct nlm_rqst *) task->tk_calldata; + + if (task->tk_status < 0) { + dprintk("lockd: %4d callback failed (errno = %d)\n", + task->tk_pid, -task->tk_status); + } + nlm_release_host(call->a_host); + rpc_release_task(task); + kfree(call); +} + +/* + * NLM Server procedures. + */ +#define nlmsvc_proc_none NULL +#define nlmsvc_encode_norep NULL +#define nlmsvc_decode_norep NULL +#define nlmsvc_decode_testres NULL +#define nlmsvc_proc_test_res NULL +#define nlmsvc_proc_lock_res NULL +#define nlmsvc_proc_cancel_res NULL +#define nlmsvc_proc_unlock_res NULL +#define nlmsvc_proc_granted_res NULL +struct nlm_void { int dummy; }; + +#define PROC(name, xargt, xrest, argt, rest) \ + { (svc_procfunc) nlmsvc_proc_##name, \ + (kxdrproc_t) nlmsvc_decode_##xargt, \ + (kxdrproc_t) nlmsvc_encode_##xrest, \ + NULL, \ + sizeof(struct nlm_##argt), \ + sizeof(struct nlm_##rest), \ + 0, \ + 0 \ + } +struct svc_procedure nlmsvc_procedures[] = { + PROC(null, void, void, void, void), + PROC(test, testargs, testres, args, res), + PROC(lock, lockargs, res, args, res), + PROC(cancel, cancargs, res, args, res), + PROC(unlock, unlockargs, res, args, res), + PROC(granted, testargs, res, args, res), + PROC(test_msg, testargs, norep, args, void), + PROC(lock_msg, lockargs, norep, args, void), + PROC(cancel_msg, cancargs, norep, args, void), + PROC(unlock_msg, unlockargs, norep, args, void), + PROC(granted_msg, testargs, norep, args, void), + PROC(test_res, testres, norep, res, void), + PROC(lock_res, res, norep, res, void), + PROC(cancel_res, res, norep, res, void), + PROC(unlock_res, res, norep, res, void), + PROC(granted_res, res, norep, res, void), + PROC(none, void, void, void, void), + PROC(none, void, void, void, void), + PROC(none, void, void, void, void), + PROC(none, void, void, void, void), + PROC(share, shareargs, shareres, args, res), + PROC(unshare, shareargs, shareres, args, res), + PROC(nm_lock, lockargs, res, args, res), + PROC(free_all, notify, void, args, void), + + /* statd callback */ + PROC(sm_notify, reboot, void, reboot, void), +}; diff -u --recursive --new-file v2.1.31/linux/fs/lockd/svcshare.c linux/fs/lockd/svcshare.c --- v2.1.31/linux/fs/lockd/svcshare.c Wed Dec 31 16:00:00 1969 +++ linux/fs/lockd/svcshare.c Fri Apr 4 11:06:05 1997 @@ -0,0 +1,111 @@ +/* + * linux/fs/lockd/svcshare.c + * + * Management of DOS shares. + * + * Copyright (C) 1996 Olaf Kirch + */ + +#include +#include +#include + +#include +#include +#include +#include + +static inline int +nlm_cmp_owner(struct nlm_share *share, struct xdr_netobj *oh) +{ + return share->s_owner.len == oh->len + && !memcmp(share->s_owner.data, oh->data, oh->len); +} + +u32 +nlmsvc_share_file(struct nlm_host *host, struct nlm_file *file, + struct nlm_args *argp) +{ + struct nlm_share *share; + struct xdr_netobj *oh = &argp->lock.oh; + u8 *ohdata; + + for (share = file->f_shares; share; share = share->s_next) { + if (share->s_host == host && nlm_cmp_owner(share, oh)) + goto update; + if ((argp->fsm_access & share->s_mode) + || (argp->fsm_mode & share->s_access )) + return nlm_lck_denied; + } + + share = (struct nlm_share *) kmalloc(sizeof(*share) + oh->len, + GFP_KERNEL); + if (share == NULL) + return nlm_lck_denied_nolocks; + + /* Copy owner handle */ + ohdata = (u8 *) (share + 1); + memcpy(ohdata, oh->data, oh->len); + + share->s_file = file; + share->s_host = host; + share->s_owner.data = ohdata; + share->s_owner.len = oh->len; + share->s_next = file->f_shares; + file->f_shares = share; + file->f_count += 1; + +update: + share->s_access = argp->fsm_access; + share->s_mode = argp->fsm_mode; + return nlm_granted; +} + +/* + * Delete a share. + */ +u32 +nlmsvc_unshare_file(struct nlm_host *host, struct nlm_file *file, + struct nlm_args *argp) +{ + struct nlm_share *share, **shpp; + struct xdr_netobj *oh = &argp->lock.oh; + + for (shpp = &file->f_shares; (share = *shpp); shpp = &share->s_next) { + if (share->s_host == host && nlm_cmp_owner(share, oh)) { + *shpp = share->s_next; + kfree(share); + return nlm_granted; + } + } + + /* X/Open spec says return success even if there was no + * corresponding share. */ + return nlm_granted; +} + +/* + * Traverse all shares for a given file (and host). + * NLM_ACT_CHECK is handled by nlmsvc_inspect_file. + */ +int +nlmsvc_traverse_shares(struct nlm_host *host, struct nlm_file *file, int action) +{ + struct nlm_share *share, **shpp; + + shpp = &file->f_shares; + while ((share = *shpp) != NULL) { + if (action == NLM_ACT_MARK) + share->s_host->h_inuse = 1; + else if (action == NLM_ACT_UNLOCK) { + if (host == NULL || host == share->s_host) { + *shpp = share->s_next; + kfree(share); + continue; + } + } + shpp = &share->s_next; + } + + return 0; +} diff -u --recursive --new-file v2.1.31/linux/fs/lockd/svcsubs.c linux/fs/lockd/svcsubs.c --- v2.1.31/linux/fs/lockd/svcsubs.c Wed Dec 31 16:00:00 1969 +++ linux/fs/lockd/svcsubs.c Fri Apr 4 11:06:05 1997 @@ -0,0 +1,278 @@ +/* + * linux/fs/lockd/svcsubs.c + * + * Various support routines for the NLM server. + * + * Copyright (C) 1996, Olaf Kirch + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NLMDBG_FACILITY NLMDBG_SVCSUBS + + +/* + * Global file hash table + */ +#define FILE_NRHASH 32 +#define FILE_HASH(dev, ino) (((dev) + (ino)) & FILE_NRHASH) +static struct nlm_file * nlm_files[FILE_NRHASH]; +static struct semaphore nlm_file_sema = MUTEX; + +/* + * Lookup file info. If it doesn't exist, create a file info struct + * and open a (VFS) file for the given inode. + * + * FIXME: + * Note that we open the file O_RDONLY even when creating write locks. + * This is not quite right, but for now, we assume the client performs + * the proper R/W checking. + */ +u32 +nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, + struct nfs_fh *f) +{ + struct nlm_file *file; + struct knfs_fh *fh = (struct knfs_fh *) f; + unsigned int hash = FILE_HASH(fh->fh_dev, fh->fh_ino); + u32 nfserr; + + dprintk("lockd: nlm_file_lookup(%04x/%ld)\n", fh->fh_dev, fh->fh_ino); + + /* Lock file table */ + down(&nlm_file_sema); + + for (file = nlm_files[hash]; file; file = file->f_next) { + if (file->f_handle.fh_ino == fh->fh_ino + && !memcmp(&file->f_handle, fh, sizeof(*fh))) + goto found; + } + + dprintk("lockd: creating file for %04x/%ld\n", fh->fh_dev, fh->fh_ino); + if (!(file = (struct nlm_file *) kmalloc(sizeof(*file), GFP_KERNEL))) { + up(&nlm_file_sema); + return nlm_lck_denied_nolocks; + } + + memset(file, 0, sizeof(*file)); + file->f_handle = *fh; + file->f_sema = MUTEX; + + /* Open the file. Note that this must not sleep for too long, else + * we would lock up lockd:-) So no NFS re-exports, folks. */ + if ((nfserr = nlmsvc_ops->fopen(rqstp, fh, &file->f_file)) != 0) { + dprintk("lockd: open failed (nfserr %ld)\n", ntohl(nfserr)); + kfree(file); + up(&nlm_file_sema); + return nlm_lck_denied; + } + + file->f_next = nlm_files[hash]; + nlm_files[hash] = file; + +found: + dprintk("lockd: found file %p (count %d)\n", file, file->f_count); + *result = file; + up(&nlm_file_sema); + file->f_count++; + return 0; +} + +/* + * Delete a file after having released all locks, blocks and shares + */ +static inline void +nlm_delete_file(struct nlm_file *file) +{ + struct inode *inode = nlmsvc_file_inode(file); + struct nlm_file **fp, *f; + + dprintk("lockd: closing file %04x/%ld\n", inode->i_dev, inode->i_ino); + fp = nlm_files + FILE_HASH(inode->i_dev, inode->i_ino); + while ((f = *fp) != NULL) { + if (f == file) { + *fp = file->f_next; + nlmsvc_ops->fclose(&file->f_file); + kfree(file); + return; + } + fp = &file->f_next; + } + + printk(KERN_WARNING "lockd: attempt to release unknown file!\n"); +} + +/* + * Loop over all locks on the given file and perform the specified + * action. + */ +static int +nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file, int action) +{ + struct inode *inode = nlmsvc_file_inode(file); + struct file_lock *fl; + struct nlm_host *lockhost; + +again: + file->f_locks = 0; + for (fl = inode->i_flock; fl; fl = fl->fl_next) { + if (!(fl->fl_flags & FL_LOCKD)) + continue; + + /* update current lock count */ + file->f_locks++; + lockhost = (struct nlm_host *) fl->fl_owner; + if (action == NLM_ACT_MARK) + lockhost->h_inuse = 1; + else if (action == NLM_ACT_CHECK) + return 1; + else if (action == NLM_ACT_UNLOCK) { + struct file_lock lock = *fl; + + if (host && lockhost != host) + continue; + + lock.fl_type = F_UNLCK; + lock.fl_start = 0; + lock.fl_end = NLM_OFFSET_MAX; + if (posix_lock_file(&file->f_file, &lock, 0) < 0) { + printk("lockd: unlock failure in %s:%d\n", + __FILE__, __LINE__); + return 1; + } + goto again; + } + } + + return 0; +} + +/* + * Operate on a single file + */ +static inline int +nlm_inspect_file(struct nlm_host *host, struct nlm_file *file, int action) +{ + if (action == NLM_ACT_CHECK) { + /* Fast path for mark and sweep garbage collection */ + if (file->f_count || file->f_blocks || file->f_shares) + return 1; + } else { + if (nlmsvc_traverse_blocks(host, file, action) + || nlmsvc_traverse_shares(host, file, action)) + return 1; + } + return nlm_traverse_locks(host, file, action); +} + +/* + * Loop over all files in the file table. + */ +static int +nlm_traverse_files(struct nlm_host *host, int action) +{ + struct nlm_file *file, **fp; + int i; + + down(&nlm_file_sema); + for (i = 0; i < FILE_NRHASH; i++) { + fp = nlm_files + i; + while ((file = *fp) != NULL) { + /* Traverse locks, blocks and shares of this file + * and update file->f_locks count */ + if (nlm_inspect_file(host, file, action)) { + up(&nlm_file_sema); + return 1; + } + + /* No more references to this file. Let go of it. */ + if (!file->f_blocks && !file->f_locks + && !file->f_shares && !file->f_count) { + *fp = file->f_next; + nlmsvc_ops->fclose(&file->f_file); + kfree(file); + } else { + fp = &file->f_next; + } + } + } + up(&nlm_file_sema); + return 0; +} + +/* + * Release file. If there are no more remote locks on this file, + * close it and free the handle. + * + * Note that we can't do proper reference counting without major + * contortions because the code in fs/locks.c creates, deletes and + * splits locks without notification. Our only way is to walk the + * entire lock list each time we remove a lock. + */ +void +nlm_release_file(struct nlm_file *file) +{ + dprintk("lockd: nlm_release_file(%p, ct = %d)\n", + file, file->f_count); + + /* Lock file table */ + down(&nlm_file_sema); + + /* If there are no more locks etc, delete the file */ + if (--(file->f_count) == 0 + && !nlm_inspect_file(NULL, file, NLM_ACT_CHECK)) + nlm_delete_file(file); + + up(&nlm_file_sema); + return; +} + +/* + * Mark all hosts that still hold resources + */ +void +nlmsvc_mark_resources(void) +{ + dprintk("lockd: nlmsvc_mark_resources\n"); + + nlm_traverse_files(NULL, NLM_ACT_MARK); +} + +/* + * Release all resources held by the given client + */ +void +nlmsvc_free_host_resources(struct nlm_host *host) +{ + dprintk("lockd: nlmsvc_free_host_resources\n"); + + if (nlm_traverse_files(host, NLM_ACT_UNLOCK)) + printk(KERN_WARNING + "lockd: couldn't remove all locks held by %s", + host->h_name); +} + +/* + * Delete a client when the nfsd entry is removed. + */ +void +nlmsvc_invalidate_client(struct svc_client *clnt) +{ + struct nlm_host *host; + + if ((host = nlm_lookup_host(clnt, NULL, 0, 0)) != NULL) { + dprintk("lockd: invalidating client for %s\n", host->h_name); + nlmsvc_free_host_resources(host); + host->h_expires = 0; + nlm_release_host(host); + } +} diff -u --recursive --new-file v2.1.31/linux/fs/lockd/xdr.c linux/fs/lockd/xdr.c --- v2.1.31/linux/fs/lockd/xdr.c Wed Dec 31 16:00:00 1969 +++ linux/fs/lockd/xdr.c Fri Apr 4 11:06:05 1997 @@ -0,0 +1,605 @@ +/* + * linux/fs/lockd/xdr.c + * + * XDR support for lockd and the lock client. + * + * Copyright (C) 1995, 1996 Olaf Kirch + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define NLMDBG_FACILITY NLMDBG_XDR +#define NLM_MAXSTRLEN 1024 + +#define QUADLEN(len) (((len) + 3) >> 2) + + +u32 nlm_granted, nlm_lck_denied, nlm_lck_denied_nolocks, + nlm_lck_blocked, nlm_lck_denied_grace_period; + + +typedef struct nlm_args nlm_args; + +/* + * Initialization of NFS status variables + */ +void +nlmxdr_init(void) +{ + static int inited = 0; + + if (inited) + return; + + nlm_granted = htonl(NLM_LCK_GRANTED); + nlm_lck_denied = htonl(NLM_LCK_DENIED); + nlm_lck_denied_nolocks = htonl(NLM_LCK_DENIED_NOLOCKS); + nlm_lck_blocked = htonl(NLM_LCK_BLOCKED); + nlm_lck_denied_grace_period = htonl(NLM_LCK_DENIED_GRACE_PERIOD); + + inited = 1; +} + +/* + * XDR functions for basic NLM types + */ +static inline u32 * +nlm_decode_cookie(u32 *p, u32 *c) +{ + unsigned int len; + + if ((len = ntohl(*p++)) == 4) { + *c = ntohl(*p++); + } else if (len == 0) { /* hockeypux brain damage */ + *c = 0; + } else { + printk(KERN_NOTICE + "lockd: bad cookie size %d (should be 4)\n", len); + return NULL; + } + return p; +} + +static inline u32 * +nlm_encode_cookie(u32 *p, u32 c) +{ + *p++ = htonl(sizeof(c)); + *p++ = htonl(c); + return p; +} + +static inline u32 * +nlm_decode_fh(u32 *p, struct nfs_fh *f) +{ + unsigned int len; + + if ((len = ntohl(*p++)) != sizeof(*f)) { + printk(KERN_NOTICE + "lockd: bad fhandle size %x (should be %d)\n", + len, sizeof(*f)); + return NULL; + } + memcpy(f, p, sizeof(*f)); + return p + XDR_QUADLEN(sizeof(*f)); +} + +static inline u32 * +nlm_encode_fh(u32 *p, struct nfs_fh *f) +{ + *p++ = htonl(sizeof(*f)); + memcpy(p, f, sizeof(*f)); + return p + XDR_QUADLEN(sizeof(*f)); +} + +/* + * Encode and decode owner handle + */ +static inline u32 * +nlm_decode_oh(u32 *p, struct xdr_netobj *oh) +{ + return xdr_decode_netobj(p, oh); +} + +static inline u32 * +nlm_encode_oh(u32 *p, struct xdr_netobj *oh) +{ + return xdr_encode_netobj(p, oh); +} + +static inline u32 * +nlm_decode_lock(u32 *p, struct nlm_lock *lock) +{ + struct file_lock *fl = &lock->fl; + int len; + + if (!(p = xdr_decode_string(p, &lock->caller, &len, NLM_MAXSTRLEN)) + || !(p = nlm_decode_fh(p, &lock->fh)) + || !(p = nlm_decode_oh(p, &lock->oh))) + return NULL; + + memset(fl, 0, sizeof(*fl)); + fl->fl_owner = current; + fl->fl_pid = ntohl(*p++); + fl->fl_flags = FL_POSIX; + fl->fl_type = F_RDLCK; /* as good as anything else */ + fl->fl_start = ntohl(*p++); + len = ntohl(*p++); + if (len == 0 || (fl->fl_end = fl->fl_start + len - 1) < 0) + fl->fl_end = NLM_OFFSET_MAX; + return p; +} + +/* + * Encode a lock as part of an NLM call + */ +static u32 * +nlm_encode_lock(u32 *p, struct nlm_lock *lock) +{ + struct file_lock *fl = &lock->fl; + + if (!(p = xdr_encode_string(p, lock->caller)) + || !(p = nlm_encode_fh(p, &lock->fh)) + || !(p = nlm_encode_oh(p, &lock->oh))) + return NULL; + + *p++ = htonl(fl->fl_pid); + *p++ = htonl(lock->fl.fl_start); + if (lock->fl.fl_end == NLM_OFFSET_MAX) + *p++ = xdr_zero; + else + *p++ = htonl(lock->fl.fl_end - lock->fl.fl_start + 1); + + return p; +} + +/* + * Encode result of a TEST/TEST_MSG call + */ +static u32 * +nlm_encode_testres(u32 *p, struct nlm_res *resp) +{ + if (!(p = nlm_encode_cookie(p, resp->cookie))) + return 0; + *p++ = resp->status; + + if (resp->status == nlm_lck_denied) { + struct file_lock *fl = &resp->lock.fl; + + *p++ = (fl->fl_type == F_RDLCK)? xdr_zero : xdr_one; + *p++ = htonl(fl->fl_pid); + + /* Encode owner handle. */ + if (!(p = xdr_encode_netobj(p, &resp->lock.oh))) + return 0; + + *p++ = htonl(fl->fl_start); + if (fl->fl_end == NLM_OFFSET_MAX) + *p++ = xdr_zero; + else + *p++ = htonl(fl->fl_end - fl->fl_start + 1); + } + + return p; +} + +/* + * Check buffer bounds after decoding arguments + */ +static inline int +xdr_argsize_check(struct svc_rqst *rqstp, u32 *p) +{ + struct svc_buf *buf = &rqstp->rq_argbuf; + + return p - buf->base <= buf->buflen; +} + +static inline int +xdr_ressize_check(struct svc_rqst *rqstp, u32 *p) +{ + struct svc_buf *buf = &rqstp->rq_resbuf; + + buf->len = p - buf->base; + return (buf->len <= buf->buflen); +} + +/* + * First, the server side XDR functions + */ +int +nlmsvc_decode_testargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp) +{ + u32 exclusive; + + if (!(p = nlm_decode_cookie(p, &argp->cookie))) + return 0; + + exclusive = ntohl(*p++); + if (!(p = nlm_decode_lock(p, &argp->lock))) + return 0; + if (exclusive) + argp->lock.fl.fl_type = F_WRLCK; + + return xdr_argsize_check(rqstp, p); +} + +int +nlmsvc_encode_testres(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp) +{ + if (!(p = nlm_encode_testres(p, resp))) + return 0; + return xdr_ressize_check(rqstp, p); +} + +int +nlmsvc_decode_lockargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp) +{ + u32 exclusive; + + if (!(p = nlm_decode_cookie(p, &argp->cookie))) + return 0; + argp->block = ntohl(*p++); + exclusive = ntohl(*p++); + if (!(p = nlm_decode_lock(p, &argp->lock))) + return 0; + if (exclusive) + argp->lock.fl.fl_type = F_WRLCK; + argp->reclaim = ntohl(*p++); + argp->state = ntohl(*p++); + argp->monitor = 1; /* monitor client by default */ + + return xdr_argsize_check(rqstp, p); +} + +int +nlmsvc_decode_cancargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp) +{ + u32 exclusive; + + if (!(p = nlm_decode_cookie(p, &argp->cookie))) + return 0; + argp->block = ntohl(*p++); + exclusive = ntohl(*p++); + if (!(p = nlm_decode_lock(p, &argp->lock))) + return 0; + if (exclusive) + argp->lock.fl.fl_type = F_WRLCK; + return xdr_argsize_check(rqstp, p); +} + +int +nlmsvc_decode_unlockargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp) +{ + if (!(p = nlm_decode_cookie(p, &argp->cookie)) + || !(p = nlm_decode_lock(p, &argp->lock))) + return 0; + argp->lock.fl.fl_type = F_UNLCK; + return xdr_argsize_check(rqstp, p); +} + +int +nlmsvc_decode_shareargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp) +{ + struct nlm_lock *lock = &argp->lock; + int len; + + memset(lock, 0, sizeof(*lock)); + lock->fl.fl_pid = ~(u32) 0; + + if (!(p = nlm_decode_cookie(p, &argp->cookie)) + || !(p = xdr_decode_string(p, &lock->caller, &len, NLM_MAXSTRLEN)) + || !(p = nlm_decode_fh(p, &lock->fh)) + || !(p = nlm_decode_oh(p, &lock->oh))) + return 0; + argp->fsm_mode = ntohl(*p++); + argp->fsm_access = ntohl(*p++); + return xdr_argsize_check(rqstp, p); +} + +int +nlmsvc_encode_shareres(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp) +{ + if (!(p = nlm_encode_cookie(p, resp->cookie))) + return 0; + *p++ = resp->status; + *p++ = xdr_zero; /* sequence argument */ + return xdr_ressize_check(rqstp, p); +} + +int +nlmsvc_encode_res(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp) +{ + if (!(p = nlm_encode_cookie(p, resp->cookie))) + return 0; + *p++ = resp->status; + return xdr_ressize_check(rqstp, p); +} + +int +nlmsvc_decode_notify(struct svc_rqst *rqstp, u32 *p, struct nlm_args *argp) +{ + struct nlm_lock *lock = &argp->lock; + int len; + + if (!(p = xdr_decode_string(p, &lock->caller, &len, NLM_MAXSTRLEN))) + return 0; + argp->state = ntohl(*p++); + return xdr_argsize_check(rqstp, p); +} + +int +nlmsvc_decode_reboot(struct svc_rqst *rqstp, u32 *p, struct nlm_reboot *argp) +{ + if (!(p = xdr_decode_string(p, &argp->mon, &argp->len, SM_MAXSTRLEN))) + return 0; + argp->state = ntohl(*p++); + argp->addr = ntohl(*p++); + return xdr_argsize_check(rqstp, p); +} + +int +nlmsvc_decode_res(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp) +{ + if (!(p = nlm_decode_cookie(p, &resp->cookie))) + return 0; + resp->status = ntohl(*p++); + return xdr_argsize_check(rqstp, p); +} + +int +nlmsvc_decode_void(struct svc_rqst *rqstp, u32 *p, void *dummy) +{ + return xdr_argsize_check(rqstp, p); +} + +int +nlmsvc_encode_void(struct svc_rqst *rqstp, u32 *p, void *dummy) +{ + return xdr_ressize_check(rqstp, p); +} + +/* + * Now, the client side XDR functions + */ +static int +nlmclt_encode_void(struct rpc_rqst *req, u32 *p, void *ptr) +{ + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +static int +nlmclt_decode_void(struct rpc_rqst *req, u32 *p, void *ptr) +{ + return 0; +} + +static int +nlmclt_encode_testargs(struct rpc_rqst *req, u32 *p, nlm_args *argp) +{ + struct nlm_lock *lock = &argp->lock; + + if (!(p = nlm_encode_cookie(p, argp->cookie))) + return -EIO; + *p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero; + if (!(p = nlm_encode_lock(p, lock))) + return -EIO; + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +static int +nlmclt_decode_testres(struct rpc_rqst *req, u32 *p, struct nlm_res *resp) +{ + if (!(p = nlm_decode_cookie(p, &resp->cookie))) + return -EIO; + resp->status = ntohl(*p++); + if (resp->status == NLM_LCK_DENIED) { + struct file_lock *fl = &resp->lock.fl; + u32 excl, len; + + memset(&resp->lock, 0, sizeof(resp->lock)); + excl = ntohl(*p++); + fl->fl_pid = ntohl(*p++); + if (!(p = nlm_decode_oh(p, &resp->lock.oh))) + return -EIO; + + fl->fl_flags = FL_POSIX; + fl->fl_type = excl? F_WRLCK : F_RDLCK; + fl->fl_start = ntohl(*p++); + len = ntohl(*p++); + if (len == 0 || (fl->fl_end = fl->fl_start + len - 1) < 0) + fl->fl_end = NLM_OFFSET_MAX; + } + return 0; +} + + +static int +nlmclt_encode_lockargs(struct rpc_rqst *req, u32 *p, nlm_args *argp) +{ + struct nlm_lock *lock = &argp->lock; + + if (!(p = nlm_encode_cookie(p, argp->cookie))) + return -EIO; + *p++ = argp->block? xdr_one : xdr_zero; + *p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero; + if (!(p = nlm_encode_lock(p, lock))) + return -EIO; + *p++ = argp->reclaim? xdr_one : xdr_zero; + *p++ = htonl(argp->state); + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +static int +nlmclt_encode_cancargs(struct rpc_rqst *req, u32 *p, nlm_args *argp) +{ + struct nlm_lock *lock = &argp->lock; + + if (!(p = nlm_encode_cookie(p, argp->cookie))) + return -EIO; + *p++ = argp->block? xdr_one : xdr_zero; + *p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero; + if (!(p = nlm_encode_lock(p, lock))) + return -EIO; + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +static int +nlmclt_encode_unlockargs(struct rpc_rqst *req, u32 *p, nlm_args *argp) +{ + struct nlm_lock *lock = &argp->lock; + + if (!(p = nlm_encode_cookie(p, argp->cookie))) + return -EIO; + if (!(p = nlm_encode_lock(p, lock))) + return -EIO; + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +static int +nlmclt_encode_res(struct rpc_rqst *req, u32 *p, struct nlm_res *resp) +{ + if (!(p = nlm_encode_cookie(p, resp->cookie))) + return -EIO; + *p++ = resp->status; + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +static int +nlmclt_encode_testres(struct rpc_rqst *req, u32 *p, struct nlm_res *resp) +{ + if (!(p = nlm_encode_testres(p, resp))) + return -EIO; + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +static int +nlmclt_decode_res(struct rpc_rqst *req, u32 *p, struct nlm_res *resp) +{ + if (!(p = nlm_decode_cookie(p, &resp->cookie))) + return -EIO; + resp->status = ntohl(*p++); + return 0; +} + +/* + * Buffer requirements for NLM + */ +#define NLM_void_sz 0 +#define NLM_cookie_sz 2 +#define NLM_caller_sz 1+QUADLEN(sizeof(system_utsname.nodename)) +#define NLM_netobj_sz 1+QUADLEN(XDR_MAX_NETOBJ) +/* #define NLM_owner_sz 1+QUADLEN(NLM_MAXOWNER) */ +#define NLM_fhandle_sz 1+QUADLEN(NFS_FHSIZE) +#define NLM_lock_sz 3+NLM_caller_sz+NLM_netobj_sz+NLM_fhandle_sz +#define NLM_holder_sz 4+NLM_netobj_sz + +#define NLM_testargs_sz NLM_cookie_sz+1+NLM_lock_sz +#define NLM_lockargs_sz NLM_cookie_sz+4+NLM_lock_sz +#define NLM_cancargs_sz NLM_cookie_sz+2+NLM_lock_sz +#define NLM_unlockargs_sz NLM_cookie_sz+NLM_lock_sz + +#define NLM_testres_sz NLM_cookie_sz+1+NLM_holder_sz +#define NLM_res_sz NLM_cookie_sz+1 +#define NLM_norep_sz 0 + +#ifndef MAX +# define MAX(a, b) (((a) > (b))? (a) : (b)) +#endif + +/* + * For NLM, a void procedure really returns nothing + */ +#define nlmclt_decode_norep NULL + +#define PROC(proc, argtype, restype) \ + { "nlm_" #proc, \ + (kxdrproc_t) nlmclt_encode_##argtype, \ + (kxdrproc_t) nlmclt_decode_##restype, \ + MAX(NLM_##argtype##_sz, NLM_##restype##_sz) << 2 \ + } + +static struct rpc_procinfo nlm_procedures[] = { + PROC(null, void, void), + PROC(test, testargs, testres), + PROC(lock, lockargs, res), + PROC(canc, cancargs, res), + PROC(unlock, unlockargs, res), + PROC(granted, testargs, res), + PROC(test_msg, testargs, norep), + PROC(lock_msg, lockargs, norep), + PROC(canc_msg, cancargs, norep), + PROC(unlock_msg, unlockargs, norep), + PROC(granted_msg, testargs, norep), + PROC(test_res, testres, norep), + PROC(lock_res, res, norep), + PROC(canc_res, res, norep), + PROC(unlock_res, res, norep), + PROC(granted_res, res, norep), + PROC(undef, void, void), + PROC(undef, void, void), + PROC(undef, void, void), + PROC(undef, void, void), +#ifdef NLMCLNT_SUPPORT_SHARES + PROC(share, shareargs, shareres), + PROC(unshare, shareargs, shareres), + PROC(nm_lock, lockargs, res), + PROC(free_all, notify, void), +#else + PROC(undef, void, void), + PROC(undef, void, void), + PROC(undef, void, void), + PROC(undef, void, void), +#endif +}; + +static struct rpc_version nlm_version1 = { + 1, 16, nlm_procedures, +}; + +static struct rpc_version nlm_version3 = { + 3, 24, nlm_procedures, +}; + +static struct rpc_version * nlm_versions[] = { + NULL, + &nlm_version1, + NULL, + &nlm_version3, +}; + +static struct rpc_stat nlm_stats; + +struct rpc_program nlm_program = { + "lockd", + NLM_PROGRAM, + sizeof(nlm_versions) / sizeof(nlm_versions[0]), + nlm_versions, + &nlm_stats, +}; + +#ifdef LOCKD_DEBUG +char * +nlm_procname(u32 proc) +{ + if (proc < sizeof(nlm_procedures)/sizeof(nlm_procedures[0])) + return nlm_procedures[proc].p_procname; + return "unknown"; +} +#endif + diff -u --recursive --new-file v2.1.31/linux/fs/nfs/Makefile linux/fs/nfs/Makefile --- v2.1.31/linux/fs/nfs/Makefile Sat Mar 30 14:05:22 1996 +++ linux/fs/nfs/Makefile Fri Apr 4 11:05:59 1997 @@ -8,11 +8,11 @@ # Note 2! The CFLAGS definitions are now in the main makefile... O_TARGET := nfs.o -O_OBJS := proc.o sock.o rpcsock.o inode.o file.o bio.o \ - nfsiod.o dir.o symlink.o +O_OBJS := inode.o file.o read.o write.o dir.o symlink.o proc.o \ + nfs2xdr.o ifdef CONFIG_ROOT_NFS -O_OBJS += nfsroot.o + O_OBJS += nfsroot.o endif M_OBJS := $(O_TARGET) diff -u --recursive --new-file v2.1.31/linux/fs/nfs/README linux/fs/nfs/README --- v2.1.31/linux/fs/nfs/README Thu Apr 25 05:04:50 1996 +++ linux/fs/nfs/README Wed Dec 31 16:00:00 1969 @@ -1,114 +0,0 @@ - - - This is an NFS client for Linux that supports async RPC calls for - read-ahead (and hopefully soon, write-back) on regular files. - - The implementation uses a straightforward nfsiod scheme. After - trying out a number of different concepts, I finally got back to - this concept, because everything else either didn't work or gave me - headaches. It's not flashy, but it works without hacking into any - other regions of the kernel. - - - HOW TO USE - - This stuff compiles as a loadable module (I developed it on 1.3.77). - Simply type mkmodule, and insmod nfs.o. This will start four nfsiod's - at the same time (which will show up under the pseudonym of insmod in - ps-style listings). - - Alternatively, you can put it right into the kernel: remove everything - from fs/nfs, move the Makefile and all *.c to this directory, and - copy all *.h files to include/linux. - - After mounting, you should be able to watch (with tcpdump) several - RPC READ calls being placed simultaneously. - - - HOW IT WORKS - - When a process reads from a file on an NFS volume, the following - happens: - - * nfs_file_read sets file->f_reada if more than 1K is - read at once. It then calls generic_file_read. - - * generic_file_read requests one ore more pages via - nfs_readpage. - - * nfs_readpage allocates a request slot with an nfsiod - daemon, fills in the READ request, sends out the - RPC call, kicks the daemon, and returns. - If there's no free biod, nfs_readpage places the - call directly, waiting for the reply (sync readpage). - - * nfsiod calls nfs_rpc_doio to collect the reply. If the - call was successful, it sets page->uptodate and - wakes up all processes waiting on page->wait; - - This is the rough outline only. There are a few things to note: - - * Async RPC will not be tried when server->rsize < PAGE_SIZE. - - * When an error occurs, nfsiod has no way of returning - the error code to the user process. Therefore, it flags - page->error and wakes up all processes waiting on that - page (they usually do so from within generic_readpage). - - generic_readpage finds that the page is still not - uptodate, and calls nfs_readpage again. This time around, - nfs_readpage notices that page->error is set and - unconditionally does a synchronous RPC call. - - This area needs a lot of improvement, since read errors - are not that uncommon (e.g. we have to retransmit calls - if the fsuid is different from the ruid in order to - cope with root squashing and stuff like this). - - Retransmits with fsuid/ruid change should be handled by - nfsiod, but this doesn't come easily (a more general nfs_call - routine that does all this may be useful...) - - * To save some time on readaheads, we save one data copy - by frobbing the page into the iovec passed to the - RPC code so that the networking layer copies the - data into the page directly. - - This needs to be adjustable (different authentication - flavors; AUTH_NULL versus AUTH_SHORT verifiers). - - * Currently, a fixed number of nfsiod's is spawned from - within init_nfs_fs. This is problematic when running - as a loadable module, because this will keep insmod's - memory allocated. As a side-effect, you will see the - nfsiod processes listed as several insmod's when doing - a `ps.' - - * This NFS client implements server congestion control via - Van Jacobson slow start as implemented in 44BSD. I haven't - checked how well this behaves, but since Rick Macklem did - it this way, it should be okay :-) - - - WISH LIST - - After giving this thing some testing, I'd like to add some more - features: - - * Some sort of async write handling. True write-back doesn't - work with the current kernel (I think), because invalidate_pages - kills all pages, regardless of whether they're dirty or not. - Besides, this may require special bdflush treatment because - write caching on clients is really hairy. - - Alternatively, a write-through scheme might be useful where - the client enqueues the request, but leaves collecting the - results to nfsiod. Again, we need a way to pass RPC errors - back to the application. - - * Support for different authentication flavors. - - * /proc/net/nfsclnt (for nfsstat, etc.). - -March 29, 1996 -Olaf Kirch diff -u --recursive --new-file v2.1.31/linux/fs/nfs/bio.c linux/fs/nfs/bio.c --- v2.1.31/linux/fs/nfs/bio.c Sat Dec 14 03:33:17 1996 +++ linux/fs/nfs/bio.c Wed Dec 31 16:00:00 1969 @@ -1,225 +0,0 @@ -/* - * linux/fs/nfs/bio.c - * - * Block I/O for NFS - * - * Partial copy of Linus' read cache modifications to fs/nfs/file.c - * modified for async RPC by okir@monad.swb.de - * - * We do an ugly hack here in order to return proper error codes to the - * user program when a read request failed. This is a huge problem because - * generic_file_read only checks the return value of inode->i_op->readpage() - * which is usually 0 for async RPC. To overcome this obstacle, we set - * the error bit of the page to 1 when an error occurs, and make nfs_readpage - * transmit requests synchronously when encountering this. - * - * Another possible solution to this problem may be to have a cache of recent - * RPC call results indexed by page pointer, or even a result code field - * in struct page. - * - * June 96: Added retries of RPCs that seem to have failed for a transient - * reason. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#undef DEBUG_BIO -#ifdef DEBUG_BIO -#define dprintk(args...) printk(## args) -#else -#define dprintk(args...) /* nothing */ -#endif - -static inline int -do_read_nfs_sync(struct inode * inode, struct page * page) -{ - struct nfs_fattr fattr; - int result, refresh = 0; - int count = PAGE_SIZE; - int rsize = NFS_SERVER(inode)->rsize; - char *buf = (char *) page_address(page); - unsigned long pos = page->offset; - - dprintk("NFS: do_read_nfs_sync(%p)\n", page); - - set_bit(PG_locked, &page->flags); - clear_bit(PG_error, &page->flags); - - do { - if (count < rsize) - rsize = count; - result = nfs_proc_read(NFS_SERVER(inode), NFS_FH(inode), - pos, rsize, buf, &fattr); - dprintk("nfs_proc_read(%s, (%x,%lx), %ld, %d, %p) = %d\n", - NFS_SERVER(inode)->hostname, - inode->i_dev, inode->i_ino, - pos, rsize, buf, result); - /* - * Even if we had a partial success we can't mark the page - * cache valid. - */ - if (result < 0) - goto io_error; - refresh = 1; - count -= result; - pos += result; - buf += result; - if (result < rsize) - break; - } while (count); - - memset(buf, 0, count); - set_bit(PG_uptodate, &page->flags); - result = 0; - -io_error: - if (refresh) - nfs_refresh_inode(inode, &fattr); - clear_bit(PG_locked, &page->flags); - wake_up(&page->wait); - return result; -} - -/* - * This is the function to (re-) transmit an NFS readahead request - */ -static int -nfsiod_read_setup(struct nfsiod_req *req) -{ - struct inode *inode = req->rq_inode; - struct page *page = req->rq_page; - - return nfs_proc_read_request(&req->rq_rpcreq, - NFS_SERVER(inode), NFS_FH(inode), - page->offset, PAGE_SIZE, - (__u32 *) page_address(page)); -} - -/* - * This is the callback from nfsiod telling us whether a reply was - * received or some error occurred (timeout or socket shutdown). - */ -static int -nfsiod_read_result(int result, struct nfsiod_req *req) -{ - struct nfs_server *server = NFS_SERVER(req->rq_inode); - struct page *page = req->rq_page; - static int succ = 0, fail = 0; - int i; - - dprintk("BIO: received callback for page %p, result %d\n", - page, result); - - if (result >= 0) { - struct nfs_fattr fattr; - - result = nfs_proc_read_reply(&req->rq_rpcreq, &fattr); - if (result >= 0) { - nfs_refresh_inode(req->rq_inode, &fattr); - if (result < PAGE_SIZE) - memset((u8 *) page_address(page)+result, - 0, PAGE_SIZE-result); - } - } else - if (result == -ETIMEDOUT && !(server->flags & NFS_MOUNT_SOFT)) { - /* XXX: Theoretically, we'd have to increment the initial - * timeo here; but I'm not going to bother with this now - * because this old nfsiod stuff will soon die anyway. - */ - result = -EAGAIN; - } - - if (result == -EAGAIN && req->rq_retries--) { - dprintk("BIO: retransmitting request.\n"); - memset(&req->rq_rpcreq, 0, sizeof(struct rpc_ioreq)); - while (rpc_reserve(server->rsock, &req->rq_rpcreq, 1) < 0) - schedule(); - current->fsuid = req->rq_fsuid; - current->fsgid = req->rq_fsgid; - for (i = 0; i < NGROUPS; i++) - current->groups[i] = req->rq_groups[i]; - nfsiod_read_setup(req); - return 0; - } - if (result >= 0) { - set_bit(PG_uptodate, &page->flags); - succ++; - } else { - dprintk("BIO: %d successful reads, %d failures\n", succ, fail); - set_bit(PG_error, &page->flags); - fail++; - } - clear_bit(PG_locked, &page->flags); - wake_up(&page->wait); - free_page(page_address(page)); - return 1; -} - -static inline int -do_read_nfs_async(struct inode *inode, struct page *page) -{ - struct nfsiod_req *req; - int result, i; - - dprintk("NFS: do_read_nfs_async(%p)\n", page); - - set_bit(PG_locked, &page->flags); - clear_bit(PG_error, &page->flags); - - if (!(req = nfsiod_reserve(NFS_SERVER(inode)))) - return -EAGAIN; - - req->rq_retries = 5; - req->rq_callback = nfsiod_read_result; - req->rq_inode = inode; - req->rq_page = page; - - req->rq_fsuid = current->fsuid; - req->rq_fsgid = current->fsgid; - for (i = 0; i < NGROUPS; i++) - req->rq_groups[i] = current->groups[i]; - - if ((result = nfsiod_read_setup(req)) >= 0) { - page->count++; - nfsiod_enqueue(req); - } else { - dprintk("NFS: deferring async READ request.\n"); - nfsiod_release(req); - clear_bit(PG_locked, &page->flags); - wake_up(&page->wait); - } - - return result < 0? result : 0; -} - -int -nfs_readpage(struct inode *inode, struct page *page) -{ - unsigned long address; - int error = -1; - - /* In case we're called from a page fault we want to */ - /* make sure we're runnable before we schedule.. */ - current->state = TASK_RUNNING; - dprintk("NFS: nfs_readpage %08lx\n", page_address(page)); - address = page_address(page); - page->count++; - if (!PageError(page) && NFS_SERVER(inode)->rsize >= PAGE_SIZE) - error = do_read_nfs_async(inode, page); - if (error < 0) /* couldn't enqueue */ - error = do_read_nfs_sync(inode, page); - free_page(address); - return error; -} diff -u --recursive --new-file v2.1.31/linux/fs/nfs/dir.c linux/fs/nfs/dir.c --- v2.1.31/linux/fs/nfs/dir.c Sun Jan 26 02:07:44 1997 +++ linux/fs/nfs/dir.c Fri Apr 4 11:05:59 1997 @@ -6,19 +6,38 @@ * nfs directory handling functions * * 10 Apr 1996 Added silly rename for unlink --okir + * 28 Sep 1996 Improved directory cache --okir */ #include #include #include -#include #include #include #include #include #include +#include +#include -#include /* for fs functions */ +#include /* for fs functions */ + +/* + * Head for a dircache entry. Currently still very simple; when + * the cache grows larger, we will need a LRU list. + */ +struct nfs_dirent { + dev_t dev; /* device number */ + ino_t ino; /* inode number */ + u32 cookie; /* cooke of first entry */ + unsigned short valid : 1, /* data is valid */ + locked : 1; /* entry locked */ + unsigned int size; /* # of entries */ + unsigned long age; /* last used */ + unsigned long mtime; /* last attr stamp */ + struct wait_queue * wait; + struct nfs_entry * entry; +}; static int nfs_dir_open(struct inode * inode, struct file * file); static long nfs_dir_read(struct inode *, struct file *, char *, unsigned long); @@ -39,7 +58,7 @@ nfs_dir_read, /* read - bad */ NULL, /* write - bad */ nfs_readdir, /* readdir */ - NULL, /* poll - default */ + NULL, /* select - default */ NULL, /* ioctl - default */ NULL, /* mmap */ nfs_dir_open, /* open - revalidate */ @@ -64,42 +83,26 @@ NULL, /* writepage */ NULL, /* bmap */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL, /* updatepage */ + nfs_revalidate, /* revalidate */ }; -static inline void revalidate_dir(struct nfs_server * server, struct inode * dir) -{ - struct nfs_fattr fattr; - - if (jiffies - NFS_READTIME(dir) < NFS_ATTRTIMEO(dir)) - return; - - NFS_READTIME(dir) = jiffies; - if (nfs_proc_getattr(server, NFS_FH(dir), &fattr) == 0) { - nfs_refresh_inode(dir, &fattr); - if (fattr.mtime.seconds == NFS_OLDMTIME(dir)) { - if ((NFS_ATTRTIMEO(dir) <<= 1) > server->acdirmax) - NFS_ATTRTIMEO(dir) = server->acdirmax; - return; - } - NFS_OLDMTIME(dir) = fattr.mtime.seconds; - } - /* invalidate directory cache here when we _really_ start caching */ -} - -static int nfs_dir_open(struct inode * dir, struct file * file) +static int +nfs_dir_open(struct inode *dir, struct file *file) { - revalidate_dir(NFS_SERVER(dir), dir); - return 0; + dfprintk(VFS, "NFS: nfs_dir_open(%x/%ld)\n", dir->i_dev, dir->i_ino); + return nfs_revalidate_inode(NFS_SERVER(dir), dir); } -static long nfs_dir_read(struct inode *inode, struct file *filp, - char *buf, unsigned long count) +static long +nfs_dir_read(struct inode *inode, struct file *filp, char *buf, unsigned long count) { return -EISDIR; } -static struct nfs_entry *c_entry = NULL; +static struct nfs_dirent dircache[NFS_MAX_DIRCACHE]; /* * We need to do caching of directory entries to prevent an @@ -107,119 +110,222 @@ * directory is cached. This seems sufficient for most purposes. * Technically, we ought to flush the cache on close but this is * not a problem in practice. + * + * XXX: Do proper directory caching by stuffing data into the + * page cache (may require some fiddling for rsize < PAGE_SIZE). */ -static int nfs_readdir(struct inode *inode, struct file *filp, - void *dirent, filldir_t filldir) -{ - static kdev_t c_dev = 0; - static int c_ino; - static int c_size; - - int result; - int i, index = 0; - struct nfs_entry *entry; +static int +nfs_readdir(struct inode *inode, struct file *filp, void *dirent, + filldir_t filldir) +{ + static struct wait_queue *readdir_wait = NULL; + struct wait_queue **waitp = NULL; + struct nfs_dirent *cache, *free; + struct nfs_entry *entry; + unsigned long age, dead; + u32 cookie; + int ismydir, result; + int i, j, index = 0; + dfprintk(VFS, "NFS: nfs_readdir(%x/%ld)\n", inode->i_dev, inode->i_ino); if (!inode || !S_ISDIR(inode->i_mode)) { printk("nfs_readdir: inode is NULL or not a directory\n"); return -EBADF; } - revalidate_dir(NFS_SERVER(inode), inode); + if ((result = nfs_revalidate_inode(NFS_SERVER(inode), inode)) < 0) + return result; - /* initialize cache memory if it hasn't been used before */ + /* + * Try to find the entry in the cache + */ +again: + if (waitp) { + interruptible_sleep_on(waitp); + if (current->signal & ~current->blocked) + return -ERESTARTSYS; + waitp = NULL; + } + + cookie = filp->f_pos; + entry = NULL; + free = NULL; + age = ~(unsigned long) 0; + dead = jiffies - NFS_ATTRTIMEO(inode); + + for (i = 0, cache = dircache; i < NFS_MAX_DIRCACHE; i++, cache++) { + /* + dprintk("NFS: dircache[%d] valid %d locked %d\n", + i, cache->valid, cache->locked); + */ + ismydir = (cache->dev == inode->i_dev + && cache->ino == inode->i_ino); + if (cache->locked) { + if (!ismydir || cache->cookie != cookie) + continue; + dfprintk(DIRCACHE, "NFS: waiting on dircache entry\n"); + waitp = &cache->wait; + goto again; + } - if (c_entry == NULL) { - i = sizeof (struct nfs_entry)*NFS_READDIR_CACHE_SIZE; - c_entry = (struct nfs_entry *) kmalloc(i, GFP_KERNEL); - if (c_entry == NULL) { - printk("nfs_readdir: no MEMORY for cache\n"); - return -ENOMEM; - } - for (i = 0; i < NFS_READDIR_CACHE_SIZE; i++) { - c_entry[i].name = (char *) kmalloc(NFS_MAXNAMLEN + 1, - GFP_KERNEL); - if (c_entry[i].name == NULL) { - printk("nfs_readdir: no MEMORY for cache\n"); - while (--i>=0) - kfree(c_entry[i].name); - kfree(c_entry); - c_entry = NULL; - return -ENOMEM; - } + if (ismydir && cache->mtime != NFS_OLDMTIME(inode)) + cache->valid = 0; + + if (!cache->valid || cache->age < dead) { + free = cache; + age = 0; + } else if (cache->age < age) { + free = cache; + age = cache->age; } - } - entry = NULL; - /* try to find it in the cache */ + if (!ismydir || !cache->valid) + continue; - if (inode->i_dev == c_dev && inode->i_ino == c_ino) { - for (i = 0; i < c_size; i++) { - if (filp->f_pos == c_entry[i].cookie) { - if (i == c_size - 1) { - if (c_entry[i].eof) - return 0; - } - else - entry = c_entry + (index = i + 1); - break; + if (cache->cookie == cookie && cache->size > 0) { + entry = cache->entry + (index = 0); + cache->locked = 1; + break; + } + for (j = 0; j < cache->size; j++) { + /* + dprintk("NFS: examing entry %.*s @%d\n", + (int) cache->entry[j].length, + cache->entry[j].name, + cache->entry[j].cookie); + */ + if (cache->entry[j].cookie != cookie) + continue; + if (j < cache->size - 1) { + entry = cache->entry + (index = j + 1); + } else if (cache->entry[j].eof) { + return 0; } + break; + } + if (entry) { + dfprintk(DIRCACHE, "NFS: found dircache entry %d\n", + cache - dircache); + cache->locked = 1; + break; } } - /* if we didn't find it in the cache, revert to an nfs call */ - - if (!entry) { - result = nfs_proc_readdir(NFS_SERVER(inode), NFS_FH(inode), - filp->f_pos, NFS_READDIR_CACHE_SIZE, c_entry); - if (result < 0) { - c_dev = 0; - return result; + /* + * Okay, entry not present in cache, or locked and inaccessible. + * Set up the cache entry and attempt a READDIR call. + */ + if (entry == NULL) { + if ((cache = free) == NULL) { + dfprintk(DIRCACHE, "NFS: dircache contention\n"); + waitp = &readdir_wait; + goto again; } - if (result > 0) { - c_dev = inode->i_dev; - c_ino = inode->i_ino; - c_size = result; - entry = c_entry + (index = 0); + dfprintk(DIRCACHE, "NFS: using free dircache entry %d\n", + free - dircache); + cache->cookie = cookie; + cache->locked = 1; + cache->valid = 0; + cache->dev = inode->i_dev; + cache->ino = inode->i_ino; + if (!cache->entry) { + cache->entry = (struct nfs_entry *) + get_free_page(GFP_KERNEL); + if (!cache->entry) { + result = -ENOMEM; + goto done; + } } - } - /* if we found it in the cache or from an nfs call, return results */ - if (!entry) - return 0; - while (index < c_size) { - int nextpos = entry->cookie; - if (filldir(dirent, entry->name, strlen(entry->name), filp->f_pos, entry->fileid) < 0) - break; - filp->f_pos = nextpos; - /* revalidate the cache if we slept in filldir() */ - if (inode->i_dev != c_dev) - break; - if (inode->i_ino != c_ino) + result = nfs_proc_readdir(NFS_SERVER(inode), NFS_FH(inode), + cookie, PAGE_SIZE, cache->entry); + if (result <= 0) + goto done; + cache->size = result; + cache->valid = 1; + entry = cache->entry + (index = 0); + } + cache->mtime = NFS_OLDMTIME(inode); + cache->age = jiffies; + + /* + * Yowza! We have a cache entry... + */ + while (index < cache->size) { + int nextpos = entry->cookie; + + /* + dprintk("NFS: filldir(%p, %.*s, %d, %d, %x, eof %x)\n", entry, + (int) entry->length, entry->name, entry->length, + (unsigned int) filp->f_pos, + entry->fileid, entry->eof); + */ + + if (filldir(dirent, entry->name, entry->length, cookie, entry->fileid) < 0) break; - if (nextpos != entry->cookie) + cookie = nextpos; + if (nextpos != entry->cookie) { + printk("nfs_readdir: shouldn't happen!\n"); break; + } index++; entry++; } - return 0; + filp->f_pos = cookie; + result = 0; + + /* XXX: May want to kick async readdir-ahead here. Not too hard + * to do. */ + +done: + dfprintk(DIRCACHE, "NFS: nfs_readdir complete\n"); + cache->locked = 0; + wake_up(&cache->wait); + wake_up(&readdir_wait); + + return result; } /* - * free cache memory - * called from cleanup_module + * Invalidate dircache entries for inode */ +void +nfs_invalidate_dircache(struct inode *inode) +{ + struct nfs_dirent *cache; + dev_t dev = inode->i_dev; + ino_t ino = inode->i_ino; + int i; + + dfprintk(DIRCACHE, "NFS: invalidate dircache for %x/%ld\n", dev, ino); + for (i = 0, cache = dircache; i < NFS_MAX_DIRCACHE; i++, cache++) { + if (!cache->locked && cache->dev == dev && cache->ino == ino) + cache->valid = 0; /* brute force */ + } +} -void nfs_kfree_cache(void) +/* + * Free directory cache memory + * Called from cleanup_module + */ +void +nfs_free_dircache(void) { - int i; + struct nfs_dirent *cache; + int i; - if (c_entry == NULL) - return; - for (i = 0; i < NFS_READDIR_CACHE_SIZE; i++) - kfree(c_entry[i].name); - kfree(c_entry); - c_entry = NULL; + dfprintk(DIRCACHE, "NFS: freeing dircache\n"); + for (i = 0, cache = dircache; i < NFS_MAX_DIRCACHE; i++, cache++) { + cache->valid = 0; + if (cache->locked) { + printk("nfs_kfree_cache: locked entry in dircache!\n"); + continue; + } + if (cache->entry) + free_page((unsigned long) cache->entry); + cache->entry = NULL; + } } @@ -234,15 +340,18 @@ * Since the cache is not hashed yet, it is a good idea not to make it too * large because every lookup looks through the entire cache even * though most of them will fail. + * + * FIXME: The lookup cache should also cache failed lookups. This can + * be a considerable win on diskless clients. */ static struct nfs_lookup_cache_entry { - kdev_t dev; - int inode; - char filename[NFS_MAXNAMLEN + 1]; - struct nfs_fh fhandle; + kdev_t dev; + ino_t inode; + char filename[NFS_MAXNAMLEN + 1]; + struct nfs_fh fhandle; struct nfs_fattr fattr; - int expiration_date; + int expiration_date; } nfs_lookup_cache[NFS_LOOKUP_CACHE_SIZE]; static struct nfs_lookup_cache_entry *nfs_lookup_cache_index(struct inode *dir, @@ -269,6 +378,8 @@ struct nfs_lookup_cache_entry *entry; + dfprintk(LOOKUPCACHE, "NFS: lookup_cache_lookup(%x/%ld, %s)\n", + dir->i_dev, dir->i_ino, filename); if (!nfs_lookup_cache_in_use) { memset(nfs_lookup_cache, 0, sizeof(nfs_lookup_cache)); nfs_lookup_cache_in_use = 1; @@ -292,6 +403,9 @@ static int nfs_lookup_cache_pos = 0; struct nfs_lookup_cache_entry *entry; + dfprintk(LOOKUPCACHE, "NFS: lookup_cache_add(%x/%ld, %s\n", + dir->i_dev, dir->i_ino, filename); + /* compensate for bug in SGI NFS server */ if (fattr->size == -1 || fattr->uid == -1 || fattr->gid == -1 || fattr->atime.seconds == -1 || fattr->mtime.seconds == -1) @@ -301,6 +415,7 @@ if (nfs_lookup_cache_pos == NFS_LOOKUP_CACHE_SIZE) nfs_lookup_cache_pos = 0; } + entry->dev = dir->i_dev; entry->inode = dir->i_ino; strcpy(entry->filename, filename); @@ -314,9 +429,9 @@ const char *filename) { struct nfs_lookup_cache_entry *entry; - kdev_t dev; - int fileid; - int i; + kdev_t dev; + ino_t fileid; + int i; if (inode) { dev = inode->i_dev; @@ -328,6 +443,10 @@ } else return; + + dfprintk(LOOKUPCACHE, "NFS: lookup_cache_remove(%x/%ld)\n", + dev, fileid); + for (i = 0; i < NFS_LOOKUP_CACHE_SIZE; i++) { entry = nfs_lookup_cache + i; if (entry->dev == dev && entry->fattr.fileid == fileid) @@ -358,6 +477,9 @@ char name[len > NFS_MAXNAMLEN? 1 : len+1]; int error; + dfprintk(VFS, "NFS: lookup(%x/%ld, %.*s)\n", + dir->i_dev, dir->i_ino, len, __name); + *result = NULL; if (!dir || !S_ISDIR(dir->i_mode)) { printk("nfs_lookup: inode is NULL or not a directory\n"); @@ -399,6 +521,9 @@ struct nfs_fh fhandle; int error; + dfprintk(VFS, "NFS: create(%x/%ld, %s\n", + dir->i_dev, dir->i_ino, name); + *result = NULL; if (!dir || !S_ISDIR(dir->i_mode)) { printk("nfs_create: inode is NULL or not a directory\n"); @@ -422,6 +547,7 @@ return -EACCES; } nfs_lookup_cache_add(dir, name, &fhandle, &fattr); + nfs_invalidate_dircache(dir); iput(dir); return 0; } @@ -434,6 +560,9 @@ struct nfs_fh fhandle; int error; + dfprintk(VFS, "NFS: mknod(%x/%ld, %s\n", + dir->i_dev, dir->i_ino, name); + if (!dir || !S_ISDIR(dir->i_mode)) { printk("nfs_mknod: inode is NULL or not a directory\n"); iput(dir); @@ -453,12 +582,8 @@ error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir), name, &sattr, &fhandle, &fattr); if (!error) - { nfs_lookup_cache_add(dir, name, &fhandle, &fattr); - /* The parent dir inode count may have changed ! */ - nfs_lookup_cache_remove( NULL, dir, NULL); - } - + nfs_invalidate_dircache(dir); iput(dir); return error; } @@ -470,6 +595,9 @@ struct nfs_fh fhandle; int error; + dfprintk(VFS, "NFS: mkdir(%x/%ld, %s\n", + dir->i_dev, dir->i_ino, name); + if (!dir || !S_ISDIR(dir->i_mode)) { printk("nfs_mkdir: inode is NULL or not a directory\n"); iput(dir); @@ -484,12 +612,9 @@ sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; error = nfs_proc_mkdir(NFS_SERVER(dir), NFS_FH(dir), name, &sattr, &fhandle, &fattr); - if (!error) { - if (fattr.fileid == dir->i_ino) - printk("Sony NewsOS 4.1R buggy nfs server?\n"); - else - nfs_lookup_cache_add(dir, name, &fhandle, &fattr); - } + if (!error) + nfs_lookup_cache_add(dir, name, &fhandle, &fattr); + nfs_invalidate_dircache(dir); iput(dir); return error; } @@ -498,6 +623,9 @@ { int error; + dfprintk(VFS, "NFS: rmdir(%x/%ld, %s\n", + dir->i_dev, dir->i_ino, name); + if (!dir || !S_ISDIR(dir->i_mode)) { printk("nfs_rmdir: inode is NULL or not a directory\n"); iput(dir); @@ -508,7 +636,9 @@ return -ENAMETOOLONG; } error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dir), name); - nfs_lookup_cache_remove(dir, NULL, name); + if (!error) + nfs_lookup_cache_remove(dir, NULL, name); + nfs_invalidate_dircache(dir); iput(dir); return error; } @@ -522,46 +652,78 @@ dir->i_count++; if (nfs_lookup(dir, name, len, &inode) < 0) return -EIO; /* arbitrary */ - if (inode->i_count == 1 || NFS_RENAMED_DIR(inode)) { + + if (inode->i_count == 1) { + iput(inode); + return -EIO; + } + if (NFS_RENAMED_DIR(inode)) { + iput(NFS_RENAMED_DIR(inode)); + NFS_RENAMED_DIR(inode) = NULL; iput(inode); return -EIO; } - slen = sprintf(silly, ".nfs%ld", inode->i_ino); + slen = sprintf(silly, ".nfs%ld", inode->i_ino); if (len == slen && !strncmp(name, silly, len)) { iput(inode); return -EIO; /* DWIM */ } + + dfprintk(VFS, "NFS: sillyrename(%x/%ld, %s)\n", + dir->i_dev, dir->i_ino, name); + ret = nfs_proc_rename(NFS_SERVER(dir), NFS_FH(dir), name, - NFS_FH(dir), silly, 0); + NFS_FH(dir), silly); if (ret >= 0) { nfs_lookup_cache_remove(dir, NULL, name); nfs_lookup_cache_remove(dir, NULL, silly); NFS_RENAMED_DIR(inode) = dir; dir->i_count++; } + nfs_invalidate_dircache(dir); iput(inode); return ret; } +/* + * When releasing the inode, finally remove any unlinked but open files. + * Note that we have to clear the set of pending signals temporarily; + * otherwise the RPC call will fail. + */ void nfs_sillyrename_cleanup(struct inode *inode) { + unsigned long oldsig; struct inode *dir = NFS_RENAMED_DIR(inode); char silly[14]; int error, slen; + dfprintk(VFS, "NFS: sillyrename cleanup(%x/%ld)\n", + inode->i_dev, inode->i_ino); + + oldsig = current->signal; + current->signal = 0; + slen = sprintf(silly, ".nfs%ld", inode->i_ino); - if ((error = nfs_unlink(dir, silly, slen)) < 0) { - printk("NFS silly_rename cleanup failed (err = %d)\n", - -error); - } + error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dir), silly); + if (error < 0) + printk("NFS: silly_rename cleanup failed (err %d)\n", -error); + + nfs_lookup_cache_remove(dir, NULL, silly); + nfs_invalidate_dircache(dir); NFS_RENAMED_DIR(inode) = NULL; + iput(dir); + + current->signal |= oldsig; } static int nfs_unlink(struct inode *dir, const char *name, int len) { int error; + dfprintk(VFS, "NFS: unlink(%x/%ld, %s)\n", + dir->i_dev, dir->i_ino, name); + if (!dir || !S_ISDIR(dir->i_mode)) { printk("nfs_unlink: inode is NULL or not a directory\n"); iput(dir); @@ -573,8 +735,10 @@ } if ((error = nfs_sillyrename(dir, name, len)) < 0) { error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dir), name); - nfs_lookup_cache_remove(dir, NULL, name); + if (!error) + nfs_lookup_cache_remove(dir, NULL, name); } + nfs_invalidate_dircache(dir); iput(dir); return error; } @@ -585,6 +749,9 @@ struct nfs_sattr sattr; int error; + dfprintk(VFS, "NFS: symlink(%x/%ld, %s, %s)\n", + dir->i_dev, dir->i_ino, name, symname); + if (!dir || !S_ISDIR(dir->i_mode)) { printk("nfs_symlink: inode is NULL or not a directory\n"); iput(dir); @@ -603,6 +770,7 @@ sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dir), name, symname, &sattr); + nfs_invalidate_dircache(dir); iput(dir); return error; } @@ -612,6 +780,10 @@ { int error; + dfprintk(VFS, "NFS: link(%x/%ld -> %x/%ld, %s)\n", + oldinode->i_dev, oldinode->i_ino, + dir->i_dev, dir->i_ino, name); + if (!oldinode) { printk("nfs_link: old inode is NULL\n"); iput(oldinode); @@ -631,19 +803,35 @@ } error = nfs_proc_link(NFS_SERVER(oldinode), NFS_FH(oldinode), NFS_FH(dir), name); - - nfs_lookup_cache_remove(dir, oldinode, NULL); + if (!error) { + nfs_lookup_cache_remove(dir, oldinode, NULL); + NFS_READTIME(oldinode) = 0; /* force getattr */ + } + nfs_invalidate_dircache(dir); iput(oldinode); iput(dir); return error; } +/* + * RENAME + * FIXME: Some nfsds, like the Linux user space nfsd, may generate a + * different file handle for the same inode after a rename (e.g. when + * moving to a different directory). A fail-safe method to do so would + * be to look up old_dir/old_name, create a link to new_dir/new_name and + * rename the old file using the silly_rename stuff. This way, the original + * file in old_dir will go away when the last process iput()s the inode. + */ static int nfs_rename(struct inode *old_dir, const char *old_name, int old_len, struct inode *new_dir, const char *new_name, int new_len, int must_be_dir) { int error; + dfprintk(VFS, "NFS: rename(%x/%ld, %s -> %x/%ld, %s)\n", + old_dir->i_dev, old_dir->i_ino, old_name, + new_dir->i_dev, new_dir->i_ino, new_name); + if (!old_dir || !S_ISDIR(old_dir->i_mode)) { printk("nfs_rename: old inode is NULL or not a directory\n"); iput(old_dir); @@ -661,13 +849,20 @@ iput(new_dir); return -ENAMETOOLONG; } + + /* We don't do rename() with trailing slashes over NFS now. Hmm. */ + if (must_be_dir) + return -EINVAL; + error = nfs_proc_rename(NFS_SERVER(old_dir), NFS_FH(old_dir), old_name, - NFS_FH(new_dir), new_name, - must_be_dir); - - nfs_lookup_cache_remove(old_dir, NULL, old_name); - nfs_lookup_cache_remove(new_dir, NULL, new_name); + NFS_FH(new_dir), new_name); + if (!error) { + nfs_lookup_cache_remove(old_dir, NULL, old_name); + nfs_lookup_cache_remove(new_dir, NULL, new_name); + } + nfs_invalidate_dircache(old_dir); + nfs_invalidate_dircache(new_dir); iput(old_dir); iput(new_dir); return error; @@ -683,6 +878,9 @@ { int was_empty; + dfprintk(VFS, "NFS: refresh_inode(%x/%ld ct=%d)\n", + inode->i_dev, inode->i_ino, inode->i_count); + if (!inode || !fattr) { printk("nfs_refresh_inode: inode or fattr is NULL\n"); return; @@ -698,10 +896,16 @@ inode->i_gid = fattr->gid; /* Size changed from outside: invalidate caches on next read */ - if (inode->i_size != fattr->size) + if (inode->i_size != fattr->size) { + dfprintk(PAGECACHE, "NFS: cacheinv(%x/%ld)\n", + inode->i_dev, inode->i_ino); NFS_CACHEINV(inode); - if (NFS_OLDMTIME(inode) != fattr->mtime.seconds) + } + if (NFS_OLDMTIME(inode) != fattr->mtime.seconds) { + dfprintk(PAGECACHE, "NFS: mtime change on %x/%ld\n", + inode->i_dev, inode->i_ino); NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); + } inode->i_size = fattr->size; if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) inode->i_rdev = to_kdev_t(fattr->rdev); diff -u --recursive --new-file v2.1.31/linux/fs/nfs/file.c linux/fs/nfs/file.c --- v2.1.31/linux/fs/nfs/file.c Sun Jan 26 02:07:44 1997 +++ linux/fs/nfs/file.c Fri Apr 4 11:05:59 1997 @@ -21,30 +21,40 @@ #include #include #include -#include #include +#include #include #include +#include -#include +#include #include -static int nfs_file_mmap(struct inode *, struct file *, struct vm_area_struct *); +#define NFSDBG_FACILITY NFSDBG_FILE + +static int nfs_file_mmap(struct inode *, struct file *, + struct vm_area_struct *); static long nfs_file_read(struct inode *, struct file *, char *, unsigned long); -static long nfs_file_write(struct inode *, struct file *, const char *, unsigned long); -static int nfs_fsync(struct inode *, struct file *); +static long nfs_file_write(struct inode *, struct file *, + const char *, unsigned long); +static int nfs_file_close(struct inode *, struct file *); +static int nfs_fsync(struct inode *, struct file *); static struct file_operations nfs_file_operations = { NULL, /* lseek - default */ nfs_file_read, /* read */ nfs_file_write, /* write */ NULL, /* readdir - bad */ - NULL, /* poll - default */ + NULL, /* select - default */ NULL, /* ioctl - default */ nfs_file_mmap, /* mmap */ NULL, /* no special open is needed */ - NULL, /* release */ + nfs_file_close, /* release */ nfs_fsync, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + nfs_lock, /* lock */ }; struct inode_operations nfs_file_inode_operations = { @@ -61,97 +71,156 @@ NULL, /* readlink */ NULL, /* follow_link */ nfs_readpage, /* readpage */ - NULL, /* writepage */ + nfs_writepage, /* writepage */ NULL, /* bmap */ - NULL /* truncate */ + NULL, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ + nfs_updatepage, /* updatepage */ + nfs_revalidate, /* revalidate */ }; -static inline void revalidate_inode(struct nfs_server * server, struct inode * inode) +/* Hack for future NFS swap support */ +#ifndef IS_SWAPFILE +# define IS_SWAPFILE(inode) (0) +#endif + + +static int +nfs_file_close(struct inode *inode, struct file *file) { - struct nfs_fattr fattr; + int status; - if (jiffies - NFS_READTIME(inode) < NFS_ATTRTIMEO(inode)) - return; + dfprintk(VFS, "nfs: close(%x/%ld)\n", inode->i_dev, inode->i_ino); - NFS_READTIME(inode) = jiffies; - if (nfs_proc_getattr(server, NFS_FH(inode), &fattr) == 0) { - nfs_refresh_inode(inode, &fattr); - if (fattr.mtime.seconds == NFS_OLDMTIME(inode)) { - if ((NFS_ATTRTIMEO(inode) <<= 1) > server->acregmax) - NFS_ATTRTIMEO(inode) = server->acregmax; - return; - } - NFS_OLDMTIME(inode) = fattr.mtime.seconds; - } - invalidate_inode_pages(inode); + if ((status = nfs_flush_dirty_pages(inode, 0, 0)) < 0) + return status; + return nfs_write_error(inode); } - -static long nfs_file_read(struct inode * inode, struct file * file, - char * buf, unsigned long count) +static long +nfs_file_read(struct inode * inode, struct file * file, + char * buf, unsigned long count) { - revalidate_inode(NFS_SERVER(inode), inode); + int status; + + dfprintk(VFS, "nfs: read(%x/%ld, %lu@%lu)\n", + inode->i_dev, inode->i_ino, count, + (unsigned long) file->f_pos); + + if ((status = nfs_revalidate_inode(NFS_SERVER(inode), inode)) < 0) + return status; return generic_file_read(inode, file, buf, count); } -static int nfs_file_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma) +static int +nfs_file_mmap(struct inode * inode, struct file * file, + struct vm_area_struct * vma) { - revalidate_inode(NFS_SERVER(inode), inode); + int status; + + dfprintk(VFS, "nfs: mmap(%x/%ld)\n", inode->i_dev, inode->i_ino); + + if ((status = nfs_revalidate_inode(NFS_SERVER(inode), inode)) < 0) + return status; return generic_file_mmap(inode, file, vma); } static int nfs_fsync(struct inode *inode, struct file *file) { - return 0; + dfprintk(VFS, "nfs: fsync(%x/%ld)\n", inode->i_dev, inode->i_ino); + + return nfs_flush_dirty_pages(inode, 0, 0); } -static long nfs_file_write(struct inode *inode, struct file *file, - const char *buf, unsigned long count) +/* + * Write to a file (through the page cache). + */ +static long +nfs_file_write(struct inode *inode, struct file *file, + const char *buf, unsigned long count) { - int result, written, wsize; - struct nfs_fattr fattr; - unsigned long pos; + int result; + + dfprintk(VFS, "nfs: write(%x/%ld (%d), %lu@%lu)\n", + inode->i_dev, inode->i_ino, inode->i_count, + count, (unsigned long) file->f_pos); if (!inode) { printk("nfs_file_write: inode = NULL\n"); return -EINVAL; } + if (IS_SWAPFILE(inode)) { + printk("NFS: attempt to write to active swap file!\n"); + return -EBUSY; + } + if ((result = nfs_revalidate_inode(NFS_SERVER(inode), inode)) < 0) + return result; if (!S_ISREG(inode->i_mode)) { printk("nfs_file_write: write to non-file, mode %07o\n", inode->i_mode); return -EINVAL; } - if (count == 0) + if (count <= 0) return 0; - pos = file->f_pos; - if (file->f_flags & O_APPEND) - pos = inode->i_size; - wsize = NFS_SERVER(inode)->wsize; - result = 0; - written = 0; - while (written < count) { - int hunk = count - written; - if (hunk >= wsize) - hunk = wsize; - result = nfs_proc_write(inode, - pos, hunk, buf, &fattr); - if (result < 0) - break; - pos += hunk; - buf += hunk; - written += hunk; - if (hunk < wsize) - break; - } - if (!written) + /* Return error from previous async call */ + if ((result = nfs_write_error(inode)) < 0) return result; - file->f_pos = pos; - if (pos > inode->i_size) - inode->i_size = pos; - /* Avoid possible Solaris 2.5 nfsd bug */ - if (inode->i_ino == fattr.fileid) - nfs_refresh_inode(inode, &fattr); - return written; + + return generic_file_write(inode, file, buf, count); } +/* + * Lock a (portion of) a file + */ +int +nfs_lock(struct inode *inode, struct file *filp, int cmd, struct file_lock *fl) +{ + int status; + + dprintk("NFS: nfs_lock(f=%4x/%ld, t=%x, fl=%x, r=%ld:%ld)\n", + filp->f_inode->i_dev, filp->f_inode->i_ino, + fl->fl_type, fl->fl_flags, + fl->fl_start, fl->fl_end); + + if (!(inode = filp->f_inode)) + return -EINVAL; + + /* No mandatory locks over NFS */ + if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) + return -ENOLCK; + + /* Fake OK code if mounted without NLM support */ + if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM) + return 0; + + /* + * No BSD flocks over NFS allowed. + * Note: we could try to fake a POSIX lock request here by + * using ((u32) filp | 0x80000000) or some such as the pid. + * Not sure whether that would be unique, though, or whether + * that would break in other places. + */ + if (!fl->fl_owner || (fl->fl_flags & (FL_POSIX|FL_BROKEN)) != FL_POSIX) + return -ENOLCK; + + /* If unlocking a file region, flush dirty pages (unless we've + * been killed by a signal, that is). */ + if (cmd == F_SETLK && fl->fl_type == F_UNLCK + && !(current->signal & ~current->blocked)) { + status = nfs_flush_dirty_pages(inode, + fl->fl_start, fl->fl_end == NLM_OFFSET_MAX? 0 : + fl->fl_end - fl->fl_start + 1); + if (status < 0) + return status; + } + + if ((status = nlmclnt_proc(inode, cmd, fl)) < 0) + return status; + + /* Here, we could turn off write-back of pages in the + * locked file region */ + + return 0; +} diff -u --recursive --new-file v2.1.31/linux/fs/nfs/inode.c linux/fs/nfs/inode.c --- v2.1.31/linux/fs/nfs/inode.c Sun Jan 26 02:07:44 1997 +++ linux/fs/nfs/inode.c Sat Apr 5 12:41:04 1997 @@ -17,25 +17,21 @@ #include #include -#include -#include #include #include #include #include #include #include -#include -#include +#include +#include +#include +#include #include -#include - -/* This is for kernel_thread */ -#define __KERNEL_SYSCALLS__ -#include +# include -extern int close_fp(struct file *filp); +#define NFSDBG_FACILITY NFSDBG_VFS static int nfs_notify_change(struct inode *, struct iattr *); static void nfs_put_inode(struct inode *); @@ -43,7 +39,7 @@ static void nfs_read_inode(struct inode *); static void nfs_statfs(struct super_block *, struct statfs *, int bufsiz); -static struct super_operations nfs_sops = { +static struct super_operations nfs_sops = { nfs_read_inode, /* read inode */ nfs_notify_change, /* notify change */ NULL, /* write inode */ @@ -54,6 +50,7 @@ NULL }; + /* * The "read_inode" function doesn't actually do anything: * the real data is filled in later in nfs_fhget. Here we @@ -61,31 +58,36 @@ * (the latter makes "nfs_refresh_inode" do the right thing * wrt pipe inodes) */ -static void nfs_read_inode(struct inode * inode) +static void +nfs_read_inode(struct inode * inode) { - int rsize = inode->i_sb->u.nfs_sb.s_server.rsize; - int size = inode->i_sb->u.nfs_sb.s_server.wsize; - - if (rsize > size) - size = rsize; - inode->i_blksize = size; + inode->i_blksize = inode->i_sb->s_blocksize; inode->i_mode = 0; inode->i_op = NULL; NFS_CACHEINV(inode); } -static void nfs_put_inode(struct inode * inode) +static void +nfs_put_inode(struct inode * inode) { + dprintk("NFS: put_inode(%x/%ld)\n", inode->i_dev, inode->i_ino); + if (NFS_RENAMED_DIR(inode)) nfs_sillyrename_cleanup(inode); if (inode->i_pipe) clear_inode(inode); } -void nfs_put_super(struct super_block *sb) +void +nfs_put_super(struct super_block *sb) { - close_fp(sb->u.nfs_sb.s_server.file); - rpc_closesock(sb->u.nfs_sb.s_server.rsock); + struct rpc_clnt *rpc; + + if ((rpc = sb->u.nfs_sb.s_server.client) != NULL) + rpc_shutdown_client(rpc); + + lockd_down(); /* release rpc.lockd */ + rpciod_down(); /* release rpciod */ lock_super(sb); sb->s_dev = 0; unlock_super(sb); @@ -93,22 +95,50 @@ } /* - * The way this works is that the mount process passes a structure - * in the data argument which contains an open socket to the NFS - * server and the root file handle obtained from the server's mount - * daemon. We stash these away in the private superblock fields. - * Later we can add other mount parameters like caching values. + * Compute and set NFS server blocksize */ - -struct super_block *nfs_read_super(struct super_block *sb, void *raw_data, - int silent) +static unsigned int +nfs_block_size(unsigned int bsize, unsigned char *nrbitsp) { - struct nfs_mount_data *data = (struct nfs_mount_data *) raw_data; - struct nfs_server *server; - unsigned int fd; - struct file *filp; + if (bsize < 1024) + bsize = NFS_DEF_FILE_IO_BUFFER_SIZE; + else if (bsize >= NFS_MAX_FILE_IO_BUFFER_SIZE) + bsize = NFS_MAX_FILE_IO_BUFFER_SIZE; + + /* make sure blocksize is a power of two */ + if ((bsize & (bsize - 1)) || nrbitsp) { + unsigned int nrbits; + + for (nrbits = 31; nrbits && !(bsize & (1 << nrbits)); nrbits--) + ; + bsize = 1 << nrbits; + if (nrbitsp) + *nrbitsp = nrbits; + if (bsize < NFS_DEF_FILE_IO_BUFFER_SIZE) + bsize = NFS_DEF_FILE_IO_BUFFER_SIZE; + } + + return bsize; +} - kdev_t dev = sb->s_dev; +/* + * The way this works is that the mount process passes a structure + * in the data argument which contains the server's IP address + * and the root file handle obtained from the server's mount + * daemon. We stash these away in the private superblock fields. + */ +struct super_block * +nfs_read_super(struct super_block *sb, void *raw_data, int silent) +{ + struct nfs_mount_data *data = (struct nfs_mount_data *) raw_data; + struct sockaddr_in srvaddr; + struct nfs_server *server; + struct rpc_timeout timeparms; + struct rpc_xprt *xprt; + struct rpc_clnt *clnt; + unsigned int authflavor; + int tcp; + kdev_t dev = sb->s_dev; MOD_INC_USE_COUNT; if (!data) { @@ -117,97 +147,106 @@ MOD_DEC_USE_COUNT; return NULL; } - fd = data->fd; if (data->version != NFS_MOUNT_VERSION) { printk("nfs warning: mount version %s than kernel\n", data->version < NFS_MOUNT_VERSION ? "older" : "newer"); + if (data->version < 2) + data->namlen = 0; + if (data->version < 3) + data->bsize = 0; } - if (fd >= NR_OPEN || !(filp = current->files->fd[fd])) { - printk("nfs_read_super: invalid file descriptor\n"); - sb->s_dev = 0; - MOD_DEC_USE_COUNT; - return NULL; - } - if (!S_ISSOCK(filp->f_inode->i_mode)) { - printk("nfs_read_super: not a socket\n"); - sb->s_dev = 0; - MOD_DEC_USE_COUNT; - return NULL; - } - filp->f_count++; + lock_super(sb); - sb->s_blocksize = 1024; /* XXX */ - sb->s_blocksize_bits = 10; - sb->s_magic = NFS_SUPER_MAGIC; - sb->s_dev = dev; - sb->s_op = &nfs_sops; - server = &sb->u.nfs_sb.s_server; - server->file = filp; - server->lock = 0; - server->wait = NULL; - server->flags = data->flags; - server->rsize = data->rsize; - if (server->rsize <= 0) - server->rsize = NFS_DEF_FILE_IO_BUFFER_SIZE; - else if (server->rsize >= NFS_MAX_FILE_IO_BUFFER_SIZE) - server->rsize = NFS_MAX_FILE_IO_BUFFER_SIZE; - server->wsize = data->wsize; - if (server->wsize <= 0) - server->wsize = NFS_DEF_FILE_IO_BUFFER_SIZE; - else if (server->wsize >= NFS_MAX_FILE_IO_BUFFER_SIZE) - server->wsize = NFS_MAX_FILE_IO_BUFFER_SIZE; - server->timeo = data->timeo*HZ/10; - server->retrans = data->retrans; + server = &sb->u.nfs_sb.s_server; + sb->s_magic = NFS_SUPER_MAGIC; + sb->s_dev = dev; + sb->s_op = &nfs_sops; + sb->s_blocksize = nfs_block_size(data->bsize, &sb->s_blocksize_bits); + server->rsize = nfs_block_size(data->rsize, NULL); + server->wsize = nfs_block_size(data->wsize, NULL); + server->flags = data->flags; server->acregmin = data->acregmin*HZ; server->acregmax = data->acregmax*HZ; server->acdirmin = data->acdirmin*HZ; server->acdirmax = data->acdirmax*HZ; strcpy(server->hostname, data->hostname); + sb->u.nfs_sb.s_root = data->root; + + /* We now require that the mount process passes the remote address */ + memcpy(&srvaddr, &data->addr, sizeof(srvaddr)); + if (srvaddr.sin_addr.s_addr == INADDR_ANY) { + printk("NFS: mount program didn't pass remote address!\n"); + MOD_DEC_USE_COUNT; + return NULL; + } - /* Start of JSP NFS patch */ - /* Check if passed address in data->addr */ - if (data->addr.sin_addr.s_addr == INADDR_ANY) { /* No address passed */ - if (((struct sockaddr_in *)(&server->toaddr))->sin_addr.s_addr == INADDR_ANY) { - printk("NFS: Error passed unconnected socket and no address\n") ; - MOD_DEC_USE_COUNT; - return NULL ; - } else { - /* Need access to socket internals JSP */ - struct socket *sock; - int dummylen ; - - /* printk("NFS: using socket address\n") ;*/ - - sock = &((filp->f_inode)->u.socket_i); - - /* extract the other end of the socket into server->toaddr */ - sock->ops->getname(sock, &(server->toaddr), &dummylen, 1) ; - } + /* Which protocol do we use? */ + tcp = (data->flags & NFS_MOUNT_TCP); + + /* Initialize timeout values */ + timeparms.to_initval = data->timeo * HZ / 10; + timeparms.to_retries = data->retrans; + timeparms.to_maxval = tcp? RPC_MAX_TCP_TIMEOUT : RPC_MAX_UDP_TIMEOUT; + timeparms.to_exponential = 1; + + /* Choose authentication flavor */ + if (data->flags & NFS_MOUNT_SECURE) { + authflavor = RPC_AUTH_DES; + } else if (data->flags & NFS_MOUNT_KERBEROS) { + authflavor = RPC_AUTH_KRB; } else { - /* printk("NFS: copying passed addr to server->toaddr\n") ;*/ - memcpy((char *)&(server->toaddr),(char *)(&data->addr),sizeof(server->toaddr)); + authflavor = RPC_AUTH_UNIX; } - /* End of JSP NFS patch */ - if ((server->rsock = rpc_makesock(filp)) == NULL) { - printk("NFS: cannot create RPC socket.\n"); - MOD_DEC_USE_COUNT; - return NULL; + /* Now create transport and client */ + xprt = xprt_create_proto(tcp? IPPROTO_TCP : IPPROTO_UDP, + &srvaddr, &timeparms); + if (xprt == NULL) { + printk("NFS: cannot create RPC transport.\n"); + goto failure; } - sb->u.nfs_sb.s_root = data->root; + clnt = rpc_create_client(xprt, server->hostname, &nfs_program, + NFS_VERSION, authflavor); + if (clnt == NULL) { + printk("NFS: cannot create RPC client.\n"); + xprt_destroy(xprt); + goto failure; + } + + clnt->cl_intr = (data->flags & NFS_MOUNT_INTR)? 1 : 0; + clnt->cl_softrtry = (data->flags & NFS_MOUNT_SOFT)? 1 : 0; + clnt->cl_chatty = 1; + server->client = clnt; + + /* Fire up rpciod if not yet running */ + rpciod_up(); + + /* Unlock super block and try to get root fh attributes */ unlock_super(sb); - if (!(sb->s_mounted = nfs_fhget(sb, &data->root, NULL))) { - sb->s_dev = 0; - printk("nfs_read_super: get root inode failed\n"); - MOD_DEC_USE_COUNT; - return NULL; + + if ((sb->s_mounted = nfs_fhget(sb, &data->root, NULL)) != NULL) { + /* We're airborne */ + lockd_up(); + return sb; } - return sb; + + /* Yargs. It didn't work out. */ + printk("nfs_read_super: get root inode failed\n"); + rpc_shutdown_client(server->client); + rpciod_down(); + +failure: + MOD_DEC_USE_COUNT; + if (sb->s_lock) + unlock_super(sb); + sb->s_dev = 0; + return NULL; } -void nfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) +static void +nfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) { int error; struct nfs_fsinfo res; @@ -238,9 +277,9 @@ * We just have to be careful not to subvert iget's special handling * of mount points. */ - -struct inode *nfs_fhget(struct super_block *sb, struct nfs_fh *fhandle, - struct nfs_fattr *fattr) +struct inode * +nfs_fhget(struct super_block *sb, struct nfs_fh *fhandle, + struct nfs_fattr *fattr) { struct nfs_fattr newfattr; int error; @@ -271,39 +310,43 @@ *NFS_FH(inode) = *fhandle; nfs_refresh_inode(inode, fattr); } + dprintk("NFS: fhget(%x/%ld ct=%d)\n", + inode->i_dev, inode->i_ino, inode->i_count); + return inode; } -int nfs_notify_change(struct inode *inode, struct iattr *attr) +int +nfs_notify_change(struct inode *inode, struct iattr *attr) { struct nfs_sattr sattr; struct nfs_fattr fattr; int error; - sattr.mode = (unsigned) -1; - if (attr->ia_valid & ATTR_MODE) + sattr.mode = (u32) -1; + if (attr->ia_valid & ATTR_MODE) sattr.mode = attr->ia_mode; - sattr.uid = (unsigned) -1; + sattr.uid = (u32) -1; if (attr->ia_valid & ATTR_UID) sattr.uid = attr->ia_uid; - sattr.gid = (unsigned) -1; + sattr.gid = (u32) -1; if (attr->ia_valid & ATTR_GID) sattr.gid = attr->ia_gid; - sattr.size = (unsigned) -1; - if (attr->ia_valid & ATTR_SIZE) - sattr.size = S_ISREG(inode->i_mode) ? attr->ia_size : -1; + sattr.size = (u32) -1; + if ((attr->ia_valid & ATTR_SIZE) && S_ISREG(inode->i_mode)) + sattr.size = attr->ia_size; - sattr.mtime.seconds = sattr.mtime.useconds = (unsigned) -1; + sattr.mtime.seconds = sattr.mtime.useconds = (u32) -1; if (attr->ia_valid & ATTR_MTIME) { sattr.mtime.seconds = attr->ia_mtime; sattr.mtime.useconds = 0; } - sattr.atime.seconds = sattr.atime.useconds = (unsigned) -1; + sattr.atime.seconds = sattr.atime.useconds = (u32) -1; if (attr->ia_valid & ATTR_ATIME) { sattr.atime.seconds = attr->ia_atime; sattr.atime.useconds = 0; @@ -311,64 +354,117 @@ error = nfs_proc_setattr(NFS_SERVER(inode), NFS_FH(inode), &sattr, &fattr); - if (!error) + if (!error) { + nfs_truncate_dirty_pages(inode, sattr.size); nfs_refresh_inode(inode, &fattr); + } inode->i_dirt = 0; return error; } -/* Every kernel module contains stuff like this. */ - -static struct file_system_type nfs_fs_type = { - nfs_read_super, "nfs", 0, NULL -}; +/* + * Externally visible revalidation function + */ +int +nfs_revalidate(struct inode *inode) +{ + return nfs_revalidate_inode(NFS_SERVER(inode), inode); +} /* - * Start up an nfsiod process. This is an awful hack, because when running - * as a module, we will keep insmod's memory. Besides, the current->comm - * hack won't work in this case - * The best would be to have a syscall for nfs client control that (among - * other things) forks biod's. - * Alternatively, we might want to have the idle task spawn biod's on demand. + * This function is called whenever some part of NFS notices that + * the cached attributes have to be refreshed. + * + * This is a bit tricky because we have to make sure all dirty pages + * have been sent off to the server before calling invalidate_inode_pages. + * To make sure no other process adds more write requests while we try + * our best to flush them, we make them sleep during the attribute refresh. + * + * A very similar scenario holds for the dir cache. */ -static int run_nfsiod(void *dummy) +int +_nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) { - int ret; + struct nfs_fattr fattr; + int status; - lock_kernel(); - MOD_INC_USE_COUNT; - exit_mm(current); - current->session = 1; - current->pgrp = 1; - sprintf(current->comm, "nfsiod"); - ret = nfsiod(); - MOD_DEC_USE_COUNT; - unlock_kernel(); - return ret; + if (jiffies - NFS_READTIME(inode) < NFS_ATTRTIMEO(inode)) + return 0; + + dfprintk(PAGECACHE, "NFS: revalidating %x/%ld inode\n", + inode->i_dev, inode->i_ino); + NFS_READTIME(inode) = jiffies; + if ((status = nfs_proc_getattr(server, NFS_FH(inode), &fattr)) < 0) + goto done; + + nfs_refresh_inode(inode, &fattr); + if (fattr.mtime.seconds != NFS_OLDMTIME(inode)) { + if (!S_ISDIR(inode->i_mode)) { + /* This sends off all dirty pages off to the server. + * Note that this function must not sleep. */ + nfs_invalidate_pages(inode); + invalidate_inode_pages(inode); + } else { + nfs_invalidate_dircache(inode); + } + + NFS_OLDMTIME(inode) = fattr.mtime.seconds; + NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); + } else { + /* Update attrtimeo value */ + if ((NFS_ATTRTIMEO(inode) <<= 1) > NFS_MAXATTRTIMEO(inode)) + NFS_ATTRTIMEO(inode) = NFS_MAXATTRTIMEO(inode); + } + status = 0; + +done: + dfprintk(PAGECACHE, + "NFS: inode %x/%ld revalidation complete (status %d).\n", + inode->i_dev, inode->i_ino, status); + return status; } -int init_nfs_fs(void) +/* + * File system information + */ +static struct file_system_type nfs_fs_type = { + nfs_read_super, "nfs", 0, NULL +}; + +/* + * Initialize NFS + */ +int +init_nfs_fs(void) { - /* Fork four biod's */ - kernel_thread(run_nfsiod, NULL, 0); - kernel_thread(run_nfsiod, NULL, 0); - kernel_thread(run_nfsiod, NULL, 0); - kernel_thread(run_nfsiod, NULL, 0); +#ifdef CONFIG_PROC_FS + rpcstat_register(&nfs_rpcstat); +#endif return register_filesystem(&nfs_fs_type); } +/* + * Every kernel module contains stuff like this. + */ #ifdef MODULE + EXPORT_NO_SYMBOLS; +/* Not quite true; I just maintain it */ +MODULE_AUTHOR("Olaf Kirch "); -int init_module(void) +int +init_module(void) { return init_nfs_fs(); } -void cleanup_module(void) +void +cleanup_module(void) { +#ifdef CONFIG_PROC_FS + rpcstat_unregister(&nfs_rpcstat); +#endif unregister_filesystem(&nfs_fs_type); - nfs_kfree_cache(); + nfs_free_dircache(); } - #endif diff -u --recursive --new-file v2.1.31/linux/fs/nfs/nfs2xdr.c linux/fs/nfs/nfs2xdr.c --- v2.1.31/linux/fs/nfs/nfs2xdr.c Wed Dec 31 16:00:00 1969 +++ linux/fs/nfs/nfs2xdr.c Fri Apr 4 11:05:59 1997 @@ -0,0 +1,649 @@ +/* + * linux/fs/nfs/xdr.c + * + * XDR functions to encode/decode NFS RPC arguments and results. + * + * Copyright (C) 1992, 1993, 1994 Rick Sladkey + * Copyright (C) 1996 Olaf Kirch + */ + +#define NFS_NEED_XDR_TYPES + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NFSDBG_FACILITY NFSDBG_XDR + +#define QUADLEN(len) (((len) + 3) >> 2) +static int nfs_stat_to_errno(int stat); + +/* Mapping from NFS error code to "errno" error code. */ +#define errno_NFSERR_IO EIO + +/* + * Declare the space requirements for NFS arguments and replies as + * number of 32bit-words + */ +#define NFS_fhandle_sz 8 +#define NFS_sattr_sz 8 +#define NFS_filename_sz 1+(NFS_MAXNAMLEN>>2) +#define NFS_path_sz 1+(NFS_MAXPATHLEN>>2) +#define NFS_fattr_sz 17 +#define NFS_info_sz 5 +#define NFS_entry_sz NFS_filename_sz+3 + +#define NFS_enc_void_sz 0 +#define NFS_diropargs_sz NFS_fhandle_sz+NFS_filename_sz +#define NFS_sattrargs_sz NFS_fhandle_sz+NFS_sattr_sz +#define NFS_readargs_sz NFS_fhandle_sz+3 +#define NFS_writeargs_sz NFS_fhandle_sz+4 +#define NFS_createargs_sz NFS_diropargs_sz+NFS_sattr_sz +#define NFS_renameargs_sz NFS_diropargs_sz+NFS_diropargs_sz +#define NFS_linkargs_sz NFS_fhandle_sz+NFS_diropargs_sz +#define NFS_symlinkargs_sz NFS_diropargs_sz+NFS_path_sz+NFS_sattr_sz +#define NFS_readdirargs_sz NFS_fhandle_sz+2 + +#define NFS_dec_void_sz 0 +#define NFS_attrstat_sz 1+NFS_fattr_sz +#define NFS_diropres_sz 1+NFS_fhandle_sz+NFS_fattr_sz +#define NFS_readlinkres_sz 1+NFS_path_sz +#define NFS_readres_sz 1+NFS_fattr_sz+1 +#define NFS_stat_sz 1 +#define NFS_readdirres_sz 1 +#define NFS_statfsres_sz 1+NFS_info_sz + +/* + * Common NFS XDR functions as inlines + */ +static inline u32 * +xdr_encode_fhandle(u32 *p, struct nfs_fh *fhandle) +{ + *((struct nfs_fh *) p) = *fhandle; + return p + QUADLEN(sizeof(*fhandle)); +} + +static inline u32 * +xdr_decode_fhandle(u32 *p, struct nfs_fh *fhandle) +{ + *fhandle = *((struct nfs_fh *) p); + return p + QUADLEN(sizeof(*fhandle)); +} + +static inline u32 * +xdr_decode_string2(u32 *p, char **string, unsigned int *len, + unsigned int maxlen) +{ + *len = ntohl(*p++); + if (*len > maxlen) + return NULL; + *string = (char *) p; + return p + QUADLEN(*len); +} + +static inline u32 * +xdr_decode_fattr(u32 *p, struct nfs_fattr *fattr) +{ + fattr->type = (enum nfs_ftype) ntohl(*p++); + fattr->mode = ntohl(*p++); + fattr->nlink = ntohl(*p++); + fattr->uid = ntohl(*p++); + fattr->gid = ntohl(*p++); + fattr->size = ntohl(*p++); + fattr->blocksize = ntohl(*p++); + fattr->rdev = ntohl(*p++); + fattr->blocks = ntohl(*p++); + fattr->fsid = ntohl(*p++); + fattr->fileid = ntohl(*p++); + fattr->atime.seconds = ntohl(*p++); + fattr->atime.useconds = ntohl(*p++); + fattr->mtime.seconds = ntohl(*p++); + fattr->mtime.useconds = ntohl(*p++); + fattr->ctime.seconds = ntohl(*p++); + fattr->ctime.useconds = ntohl(*p++); + return p; +} + +static inline u32 * +xdr_encode_sattr(u32 *p, struct nfs_sattr *sattr) +{ + *p++ = htonl(sattr->mode); + *p++ = htonl(sattr->uid); + *p++ = htonl(sattr->gid); + *p++ = htonl(sattr->size); + *p++ = htonl(sattr->atime.seconds); + *p++ = htonl(sattr->atime.useconds); + *p++ = htonl(sattr->mtime.seconds); + *p++ = htonl(sattr->mtime.useconds); + return p; +} + +/* + * NFS encode functions + */ +/* + * Encode void argument + */ +static int +nfs_xdr_enc_void(struct rpc_rqst *req, u32 *p, void *dummy) +{ + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +/* + * Encode file handle argument + * GETATTR, READLINK, STATFS + */ +static int +nfs_xdr_fhandle(struct rpc_rqst *req, u32 *p, struct nfs_fh *fh) +{ + p = xdr_encode_fhandle(p, fh); + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +/* + * Encode SETATTR arguments + */ +static int +nfs_xdr_sattrargs(struct rpc_rqst *req, u32 *p, struct nfs_sattrargs *args) +{ + p = xdr_encode_fhandle(p, args->fh); + p = xdr_encode_sattr(p, args->sattr); + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +/* + * Encode directory ops argument + * LOOKUP, REMOVE, RMDIR + */ +static int +nfs_xdr_diropargs(struct rpc_rqst *req, u32 *p, struct nfs_diropargs *args) +{ + p = xdr_encode_fhandle(p, args->fh); + p = xdr_encode_string(p, args->name); + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +/* + * Arguments to a READ call. Since we read data directly into the page + * cache, we also set up the reply iovec here so that iov[1] points + * exactly to the page wewant to fetch. + */ +static int +nfs_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args) +{ + struct rpc_auth *auth = req->rq_task->tk_auth; + int replen, buflen; + + p = xdr_encode_fhandle(p, args->fh); + *p++ = htonl(args->offset); + *p++ = htonl(args->count); + *p++ = htonl(args->count); + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + +#if 1 + /* set up reply iovec */ + replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2; + buflen = req->rq_rvec[0].iov_len; + req->rq_rvec[0].iov_len = replen; + req->rq_rvec[1].iov_base = args->buffer; + req->rq_rvec[1].iov_len = args->count; + req->rq_rvec[2].iov_base = (u8 *) req->rq_rvec[0].iov_base + replen; + req->rq_rvec[2].iov_len = buflen - replen; + req->rq_rlen = args->count + buflen; + req->rq_rnr = 3; +#else + replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2; + req->rq_rvec[0].iov_len = replen; +#endif + + return 0; +} + +/* + * Decode READ reply + */ +static int +nfs_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res) +{ + struct iovec *iov = req->rq_rvec; + int status, count, recvd, hdrlen; + + dprintk("RPC: readres OK status %lx\n", ntohl(*p)); + if ((status = ntohl(*p++))) + return -nfs_stat_to_errno(status); + p = xdr_decode_fattr(p, res->fattr); + + count = ntohl(*p++); + hdrlen = (u8 *) p - (u8 *) iov->iov_base; + recvd = req->rq_rlen - hdrlen; + if (p != iov[2].iov_base) { + /* Unexpected reply header size. Punt. + * XXX: Move iovec contents to align data on page + * boundary and adjust RPC header size guess */ + printk("NFS: Odd RPC header size in read reply: %d\n", hdrlen); + return -errno_NFSERR_IO; + } + if (count > recvd) { + printk("NFS: server cheating in read reply: " + "count %d > recvd %d\n", count, recvd); + count = recvd; + } + + dprintk("RPC: readres OK count %d\n", count); + if (count < res->count) + memset((u8 *)(iov[1].iov_base+count), 0, res->count-count); + + return count; +} + + +/* + * Write arguments. Splice the buffer to be written into the iovec. + */ +static int +nfs_xdr_writeargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args) +{ + p = xdr_encode_fhandle(p, args->fh); + *p++ = htonl(args->offset); + *p++ = htonl(args->offset); + *p++ = htonl(args->count); + *p++ = htonl(args->count); + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + + req->rq_svec[1].iov_base = (void *) args->buffer; + req->rq_svec[1].iov_len = args->count; + req->rq_slen += args->count; + req->rq_snr = 2; + + return 0; +} + +/* + * Encode create arguments + * CREATE, MKDIR + */ +static int +nfs_xdr_createargs(struct rpc_rqst *req, u32 *p, struct nfs_createargs *args) +{ + p = xdr_encode_fhandle(p, args->fh); + p = xdr_encode_string(p, args->name); + p = xdr_encode_sattr(p, args->sattr); + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +/* + * Encode RENAME arguments + */ +static int +nfs_xdr_renameargs(struct rpc_rqst *req, u32 *p, struct nfs_renameargs *args) +{ + p = xdr_encode_fhandle(p, args->fromfh); + p = xdr_encode_string(p, args->fromname); + p = xdr_encode_fhandle(p, args->tofh); + p = xdr_encode_string(p, args->toname); + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +/* + * Encode LINK arguments + */ +static int +nfs_xdr_linkargs(struct rpc_rqst *req, u32 *p, struct nfs_linkargs *args) +{ + p = xdr_encode_fhandle(p, args->fromfh); + p = xdr_encode_fhandle(p, args->tofh); + p = xdr_encode_string(p, args->toname); + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +/* + * Encode SYMLINK arguments + */ +static int +nfs_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_symlinkargs *args) +{ + p = xdr_encode_fhandle(p, args->fromfh); + p = xdr_encode_string(p, args->fromname); + p = xdr_encode_string(p, args->topath); + p = xdr_encode_sattr(p, args->sattr); + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +/* + * Encode arguments to readdir call + */ +static int +nfs_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs_readdirargs *args) +{ + struct rpc_auth *auth = req->rq_task->tk_auth; + int replen; + + p = xdr_encode_fhandle(p, args->fh); + *p++ = htonl(args->cookie); + *p++ = htonl(args->bufsiz); + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + + /* set up reply iovec */ + replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readdirres_sz) << 2; + /* + dprintk("RPC: readdirargs: slack is 4 * (%d + %d + %d) = %d\n", + RPC_REPHDRSIZE, auth->au_rslack, NFS_readdirres_sz, replen); + */ + req->rq_rvec[0].iov_len = replen; + req->rq_rvec[1].iov_base = args->buffer; + req->rq_rvec[1].iov_len = args->bufsiz; + req->rq_rlen = replen + args->bufsiz; + req->rq_rnr = 2; + + /* + dprintk("RPC: readdirargs set up reply vec:\n"); + dprintk(" rvec[0] = %p/%d\n", + req->rq_rvec[0].iov_base, + req->rq_rvec[0].iov_len); + dprintk(" rvec[1] = %p/%d\n", + req->rq_rvec[1].iov_base, + req->rq_rvec[1].iov_len); + */ + + return 0; +} + +/* + * Decode the result of a readdir call. We decode the result in place + * to avoid a malloc of NFS_MAXNAMLEN+1 for each file name. + * After decoding, the layout in memory looks like this: + * entry1 entry2 ... entryN stringN ... string2 string1 + * Note that the strings are not null-terminated so that the entire number + * of entries returned by the server should fit into the buffer. + */ +static int +nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res) +{ + struct nfs_entry *entry; + struct iovec *iov = req->rq_rvec; + int status, nr, len; + char *string; + u32 *end; + + if ((status = ntohl(*p++))) + return -nfs_stat_to_errno(status); + if ((void *) p != ((u8 *) iov->iov_base+iov->iov_len)) { + /* Unexpected reply header size. Punt. */ + printk("NFS: Odd RPC header size in readdirres reply\n"); + return -errno_NFSERR_IO; + } + + /* Get start and end address of XDR data */ + p = (u32 *) iov[1].iov_base; + end = (u32 *) ((u8 *) p + iov[1].iov_len); + + /* Get start and end of dirent buffer */ + entry = (struct nfs_entry *) res->buffer; + string = (char *) res->buffer + res->bufsiz; + for (nr = 0; *p++; nr++, entry++) { + entry->fileid = ntohl(*p++); + + len = ntohl(*p++); + if ((p + QUADLEN(len) + 3) > end) { + printk(KERN_NOTICE + "NFS: short packet in readdir reply!\n"); + break; + } + if (len > NFS_MAXNAMLEN) { + printk("NFS: giant filename in readdir (len %x)!\n", + len); + return -errno_NFSERR_IO; + } + string -= len; + if ((void *) (entry+1) > (void *) string) { + printk(KERN_NOTICE "NFS: should not happen in %s!\n", + __FUNCTION__); + break; + } + + entry->name = string; + entry->length = len; + memmove(string, p, len); + p += QUADLEN(len); + entry->cookie = ntohl(*p++); + entry->eof = !p[0] && p[1]; + } + return nr; +} + +/* + * NFS XDR decode functions + */ +/* + * Decode void reply + */ +static int +nfs_xdr_dec_void(struct rpc_rqst *req, u32 *p, void *dummy) +{ + return 0; +} + +/* + * Decode simple status reply + */ +static int +nfs_xdr_stat(struct rpc_rqst *req, u32 *p, void *dummy) +{ + int status; + + if ((status = ntohl(*p++)) != 0) + status = -nfs_stat_to_errno(status); + return status; +} + +/* + * Decode attrstat reply + * GETATTR, SETATTR, WRITE + */ +static int +nfs_xdr_attrstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr) +{ + int status; + + dprintk("RPC: attrstat status %lx\n", ntohl(*p)); + if ((status = ntohl(*p++))) + return -nfs_stat_to_errno(status); + xdr_decode_fattr(p, fattr); + dprintk("RPC: attrstat OK type %d mode %o dev %x ino %x\n", + fattr->type, fattr->mode, fattr->fsid, fattr->fileid); + return 0; +} + +/* + * Decode diropres reply + * LOOKUP, CREATE, MKDIR + */ +static int +nfs_xdr_diropres(struct rpc_rqst *req, u32 *p, struct nfs_diropok *res) +{ + int status; + + dprintk("RPC: diropres status %lx\n", ntohl(*p)); + if ((status = ntohl(*p++))) + return -nfs_stat_to_errno(status); + p = xdr_decode_fhandle(p, res->fh); + xdr_decode_fattr(p, res->fattr); + dprintk("RPC: diropres OK type %x mode %o dev %x ino %x\n", + res->fattr->type, res->fattr->mode, + res->fattr->fsid, res->fattr->fileid); + return 0; +} + +/* + * Decode READLINK reply + */ +static int +nfs_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs_readlinkres *res) +{ + int status; + + if ((status = ntohl(*p++))) + return -nfs_stat_to_errno(status); + xdr_decode_string2(p, res->string, res->lenp, res->maxlen); + + /* Caller takes over the buffer here to avoid extra copy */ + res->buffer = req->rq_task->tk_buffer; + req->rq_task->tk_buffer = NULL; + return 0; +} + +/* + * Decode STATFS reply + */ +static int +nfs_xdr_statfsres(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res) +{ + int status; + + if ((status = ntohl(*p++))) + return -nfs_stat_to_errno(status); + res->tsize = ntohl(*p++); + res->bsize = ntohl(*p++); + res->blocks = ntohl(*p++); + res->bfree = ntohl(*p++); + res->bavail = ntohl(*p++); + return 0; +} + +/* + * We need to translate between nfs status return values and + * the local errno values which may not be the same. + */ +static struct { + int stat; + int errno; +} nfs_errtbl[] = { + { NFS_OK, 0 }, + { NFSERR_PERM, EPERM }, + { NFSERR_NOENT, ENOENT }, + { NFSERR_IO, errno_NFSERR_IO }, + { NFSERR_NXIO, ENXIO }, + { NFSERR_EAGAIN, EAGAIN }, + { NFSERR_ACCES, EACCES }, + { NFSERR_EXIST, EEXIST }, + { NFSERR_NODEV, ENODEV }, + { NFSERR_NOTDIR, ENOTDIR }, + { NFSERR_ISDIR, EISDIR }, + { NFSERR_INVAL, EINVAL }, + { NFSERR_FBIG, EFBIG }, + { NFSERR_NOSPC, ENOSPC }, + { NFSERR_ROFS, EROFS }, + { NFSERR_NAMETOOLONG, ENAMETOOLONG }, + { NFSERR_NOTEMPTY, ENOTEMPTY }, + { NFSERR_DQUOT, EDQUOT }, + { NFSERR_STALE, ESTALE }, +#ifdef EWFLUSH + { NFSERR_WFLUSH, EWFLUSH }, +#endif + { -1, EIO } +}; + +static int +nfs_stat_to_errno(int stat) +{ + int i; + + for (i = 0; nfs_errtbl[i].stat != -1; i++) { + if (nfs_errtbl[i].stat == stat) + return nfs_errtbl[i].errno; + } + printk("nfs_stat_to_errno: bad nfs status return value: %d\n", stat); + return nfs_errtbl[i].errno; +} + +#ifndef MAX +# define MAX(a, b) (((a) > (b))? (a) : (b)) +#endif + +#define PROC(proc, argtype, restype) \ + { "nfs_" #proc, \ + (kxdrproc_t) nfs_xdr_##argtype, \ + (kxdrproc_t) nfs_xdr_##restype, \ + MAX(NFS_##argtype##_sz,NFS_##restype##_sz) << 2 \ + } + +static struct rpc_procinfo nfs_procedures[18] = { + PROC(null, enc_void, dec_void), + PROC(getattr, fhandle, attrstat), + PROC(setattr, sattrargs, attrstat), + PROC(root, enc_void, dec_void), + PROC(lookup, diropargs, diropres), + PROC(readlink, fhandle, readlinkres), + PROC(read, readargs, readres), + PROC(writecache, enc_void, dec_void), + PROC(write, writeargs, attrstat), + PROC(create, createargs, diropres), + PROC(remove, diropargs, stat), + PROC(rename, renameargs, stat), + PROC(link, linkargs, stat), + PROC(symlink, symlinkargs, stat), + PROC(mkdir, createargs, diropres), + PROC(rmdir, diropargs, stat), + PROC(readdir, readdirargs, readdirres), + PROC(statfs, fhandle, statfsres), +}; + +static struct rpc_version nfs_version2 = { + 2, + sizeof(nfs_procedures)/sizeof(nfs_procedures[0]), + nfs_procedures +}; + +static struct rpc_version * nfs_version[] = { + NULL, + NULL, + &nfs_version2 +}; + +struct rpc_program nfs_program = { + "nfs", + NFS_PROGRAM, + sizeof(nfs_version) / sizeof(nfs_version[0]), + nfs_version, + &nfs_rpcstat, +}; + +/* + * RPC stats support + */ +static int +nfs_get_info(char *buffer, char **start, off_t offset, int length, int dummy) +{ + return rpcstat_get_info(&nfs_rpcstat, buffer, start, offset, length); +} + +static struct proc_dir_entry proc_nfsclnt = { + 0, 3, "nfs", + S_IFREG | S_IRUGO, 1, 0, 0, + 6, &proc_net_inode_operations, + nfs_get_info +}; + +struct rpc_stat nfs_rpcstat = { + NULL, /* next */ + &proc_nfsclnt, /* /proc/net directory entry */ + &nfs_program, /* RPC program */ +}; diff -u --recursive --new-file v2.1.31/linux/fs/nfs/nfs3xdr.c linux/fs/nfs/nfs3xdr.c --- v2.1.31/linux/fs/nfs/nfs3xdr.c Wed Dec 31 16:00:00 1969 +++ linux/fs/nfs/nfs3xdr.c Fri Apr 4 11:05:59 1997 @@ -0,0 +1,669 @@ +/* + * linux/fs/nfs/nfs2xdr.c + * + * XDR functions to encode/decode NFSv3 RPC arguments and results. + * Note: this is incomplete! + * + * Copyright (C) 1996 Olaf Kirch + */ + +#define NFS_NEED_XDR_TYPES + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef RPC_DEBUG +# define RPC_FACILITY RPCDBG_NFS +#endif + +#define QUADLEN(len) (((len) + 3) >> 2) +static int nfs_stat_to_errno(int stat); + +/* Mapping from NFS error code to "errno" error code. */ +#define errno_NFSERR_IO EIO + +/* + * Declare the space requirements for NFS arguments and replies as + * number of 32bit-words + */ +#define NFS_fhandle_sz (1+16) +#define NFS_sattr_sz 8 +#define NFS_filename_sz 1+(NFS_MAXNAMLEN>>2) +#define NFS_path_sz 1+(NFS_MAXPATHLEN>>2) +#define NFS_fattr_sz 17 +#define NFS_info_sz 5 +#define NFS_entry_sz NFS_filename_sz+3 + +#define NFS_enc_void_sz 0 +#define NFS_diropargs_sz NFS_fhandle_sz+NFS_filename_sz +#define NFS_sattrargs_sz NFS_fhandle_sz+NFS_sattr_sz +#define NFS_readargs_sz NFS_fhandle_sz+3 +#define NFS_writeargs_sz NFS_fhandle_sz+4 +#define NFS_createargs_sz NFS_diropargs_sz+NFS_sattr_sz +#define NFS_renameargs_sz NFS_diropargs_sz+NFS_diropargs_sz +#define NFS_linkargs_sz NFS_fhandle_sz+NFS_diropargs_sz +#define NFS_symlinkargs_sz NFS_diropargs_sz+NFS_path_sz+NFS_sattr_sz +#define NFS_readdirargs_sz NFS_fhandle_sz+2 + +#define NFS_dec_void_sz 0 +#define NFS_attrstat_sz 1+NFS_fattr_sz +#define NFS_diropres_sz 1+NFS_fhandle_sz+NFS_fattr_sz +#define NFS_readlinkres_sz 1+NFS_path_sz +#define NFS_readres_sz 1+NFS_fattr_sz+1 +#define NFS_stat_sz 1 +#define NFS_readdirres_sz 1 +#define NFS_statfsres_sz 1+NFS_info_sz + +/* + * Common NFS XDR functions as inlines + */ +static inline u32 * +xdr_encode_fhandle(u32 *p, struct nfs3_fh *fh) +{ + *p++ = htonl(fh->size); + memcpy(p, fh->data, fh->size); + return p + QUADLEN(fh->size); +} + +static inline u32 * +xdr_decode_fhandle(u32 *p, struct nfs3_fh *fh) +{ + if ((fh->size = ntohl(*p++)) <= NFS3_FHSIZE) { + memcpy(fh->data, p, fh->size); + return p + QUADLEN(fh->size); + } + return NULL; +} + +static inline enum nfs_ftype +xdr_decode_ftype(u32 type) +{ + return (type == NF3FIFO)? NFFIFO : (enum nfs_ftype) type; +} + +static inline u32 * +xdr_decode_string2(u32 *p, char **string, unsigned int *len, + unsigned int maxlen) +{ + *len = ntohl(*p++); + if (*len > maxlen) + return NULL; + *string = (char *) p; + return p + QUADLEN(*len); +} + +static inline u32 * +xdr_decode_fattr(u32 *p, struct nfs3_fattr *fattr) +{ + fattr->type = xdr_decode_ftype(ntohl(*p++)); + fattr->mode = ntohl(*p++); + fattr->nlink = ntohl(*p++); + fattr->uid = ntohl(*p++); + fattr->gid = ntohl(*p++); + fattr->size = ((u64) ntohl(*p++) << 32) | ntohl(*p++); + fattr->used = ((u64) ntohl(*p++) << 32) | ntohl(*p++); + fattr->rdev_maj = ntohl(*p++); + fattr->rdev_min = ntohl(*p++); + fattr->fsid = ntohl(*p++); + fattr->fileid = ntohl(*p++); + fattr->atime.seconds = ntohl(*p++); + fattr->atime.useconds = ntohl(*p++); + fattr->mtime.seconds = ntohl(*p++); + fattr->mtime.useconds = ntohl(*p++); + fattr->ctime.seconds = ntohl(*p++); + fattr->ctime.useconds = ntohl(*p++); + return p; +} + +static inline u32 * +xdr_encode_sattr(u32 *p, struct nfs_sattr *sattr) +{ + *p++ = htonl(sattr->mode); + *p++ = htonl(sattr->uid); + *p++ = htonl(sattr->gid); + *p++ = htonl(sattr->size >> 32); + *p++ = htonl(sattr->size & 0xFFFFFFFF); + *p++ = htonl(sattr->atime.seconds); + *p++ = htonl(sattr->atime.useconds); + *p++ = htonl(sattr->mtime.seconds); + *p++ = htonl(sattr->mtime.useconds); + return p; +} + +/* + * NFS encode functions + */ +/* + * Encode void argument + */ +static int +nfs_xdr_enc_void(struct rpc_rqst *req, u32 *p, void *dummy) +{ + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +/* + * Encode file handle argument + * GETATTR, READLINK, STATFS + */ +static int +nfs_xdr_fhandle(struct rpc_rqst *req, u32 *p, struct nfs3_fh *fh) +{ + p = xdr_encode_fhandle(p, fh); + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +/* + * Encode SETATTR arguments + */ +static int +nfs_xdr_sattrargs(struct rpc_rqst *req, u32 *p, struct nfs_sattrargs *args) +{ + p = xdr_encode_fhandle(p, args->fh); + p = xdr_encode_sattr(p, args->sattr); + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +/* + * Encode directory ops argument + * LOOKUP, REMOVE, RMDIR + */ +static int +nfs_xdr_diropargs(struct rpc_rqst *req, u32 *p, struct nfs_diropargs *args) +{ + p = xdr_encode_fhandle(p, args->fh); + p = xdr_encode_string(p, args->name); + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +/* + * Arguments to a READ call. Since we read data directly into the page + * cache, we also set up the reply iovec here so that iov[1] points + * exactly to the page wewant to fetch. + */ +static int +nfs_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args) +{ + struct rpc_auth *auth = req->rq_task->tk_auth; + int replen, buflen; + + p = xdr_encode_fhandle(p, args->fh); + *p++ = htonl(args->offset); + *p++ = htonl(args->count); + *p++ = htonl(args->count); + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + +#if 1 + /* set up reply iovec */ + replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2; + buflen = req->rq_rvec[0].iov_len; + req->rq_rvec[0].iov_len = replen; + req->rq_rvec[1].iov_base = args->buffer; + req->rq_rvec[1].iov_len = args->count; + req->rq_rvec[2].iov_base = (u8 *) req->rq_rvec[0].iov_base + replen; + req->rq_rvec[2].iov_len = buflen - replen; + req->rq_rlen = args->count + buflen; + req->rq_rnr = 3; +#else + replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2; + req->rq_rvec[0].iov_len = replen; +#endif + + return 0; +} + +/* + * Decode READ reply + */ +static int +nfs_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res) +{ + struct iovec *iov = req->rq_rvec; + int status, count, recvd, hdrlen; + + dprintk("RPC: readres OK status %lx\n", ntohl(*p)); + if ((status = ntohl(*p++))) + return -nfs_stat_to_errno(status); + p = xdr_decode_fattr(p, res->fattr); + + count = ntohl(*p++); + hdrlen = (u8 *) p - (u8 *) iov->iov_base; + recvd = req->rq_rlen - hdrlen; + if (p != iov[2].iov_base) { + /* Unexpected reply header size. Punt. + * XXX: Move iovec contents to align data on page + * boundary and adjust RPC header size guess */ + printk("NFS: Odd RPC header size in read reply: %d\n", hdrlen); + return -errno_NFSERR_IO; + } + if (count > recvd) { + printk("NFS: server cheating in read reply: " + "count %d > recvd %d\n", count, recvd); + count = recvd; + } + + dprintk("RPC: readres OK count %d\n", count); + if (count < res->count) + memset((u8 *)(iov[1].iov_base+count), 0, res->count-count); + + return count; +} + + +/* + * Write arguments. Splice the buffer to be written into the iovec. + */ +static int +nfs_xdr_writeargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args) +{ + p = xdr_encode_fhandle(p, args->fh); + *p++ = htonl(args->offset); + *p++ = htonl(args->offset); + *p++ = htonl(args->count); + *p++ = htonl(args->count); + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + + req->rq_svec[1].iov_base = (void *) args->buffer; + req->rq_svec[1].iov_len = args->count; + req->rq_slen += args->count; + req->rq_snr = 2; + + return 0; +} + +/* + * Encode create arguments + * CREATE, MKDIR + */ +static int +nfs_xdr_createargs(struct rpc_rqst *req, u32 *p, struct nfs_createargs *args) +{ + p = xdr_encode_fhandle(p, args->fh); + p = xdr_encode_string(p, args->name); + p = xdr_encode_sattr(p, args->sattr); + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +/* + * Encode RENAME arguments + */ +static int +nfs_xdr_renameargs(struct rpc_rqst *req, u32 *p, struct nfs_renameargs *args) +{ + p = xdr_encode_fhandle(p, args->fromfh); + p = xdr_encode_string(p, args->fromname); + p = xdr_encode_fhandle(p, args->tofh); + p = xdr_encode_string(p, args->toname); + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +/* + * Encode LINK arguments + */ +static int +nfs_xdr_linkargs(struct rpc_rqst *req, u32 *p, struct nfs_linkargs *args) +{ + p = xdr_encode_fhandle(p, args->fromfh); + p = xdr_encode_fhandle(p, args->tofh); + p = xdr_encode_string(p, args->toname); + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +/* + * Encode SYMLINK arguments + */ +static int +nfs_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_symlinkargs *args) +{ + p = xdr_encode_fhandle(p, args->fromfh); + p = xdr_encode_string(p, args->fromname); + p = xdr_encode_string(p, args->topath); + p = xdr_encode_sattr(p, args->sattr); + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +/* + * Encode arguments to readdir call + */ +static int +nfs_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs_readdirargs *args) +{ + struct rpc_auth *auth = req->rq_task->tk_auth; + int replen; + + p = xdr_encode_fhandle(p, args->fh); + *p++ = htonl(args->cookie); + *p++ = htonl(args->bufsiz); + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + + /* set up reply iovec */ + replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readdirres_sz) << 2; + /* + dprintk("RPC: readdirargs: slack is 4 * (%d + %d + %d) = %d\n", + RPC_REPHDRSIZE, auth->au_rslack, NFS_readdirres_sz, replen); + */ + req->rq_rvec[0].iov_len = replen; + req->rq_rvec[1].iov_base = args->buffer; + req->rq_rvec[1].iov_len = args->bufsiz; + req->rq_rlen = replen + args->bufsiz; + req->rq_rnr = 2; + + /* + dprintk("RPC: readdirargs set up reply vec:\n"); + dprintk(" rvec[0] = %p/%d\n", + req->rq_rvec[0].iov_base, + req->rq_rvec[0].iov_len); + dprintk(" rvec[1] = %p/%d\n", + req->rq_rvec[1].iov_base, + req->rq_rvec[1].iov_len); + */ + + return 0; +} + +/* + * Decode the result of a readdir call. We decode the result in place + * to avoid a malloc of NFS_MAXNAMLEN+1 for each file name. + * After decoding, the layout in memory looks like this: + * entry1 entry2 ... entryN stringN ... string2 string1 + * Note that the strings are not null-terminated so that the entire number + * of entries returned by the server should fit into the buffer. + */ +static int +nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res) +{ + struct nfs_entry *entry; + struct iovec *iov = req->rq_rvec; + int status, nr, len; + char *string; + u32 *end; + + if ((status = ntohl(*p++))) + return -nfs_stat_to_errno(status); + if ((void *) p != ((u8 *) iov->iov_base+iov->iov_len)) { + /* Unexpected reply header size. Punt. */ + printk("NFS: Odd RPC header size in readdirres reply\n"); + return -errno_NFSERR_IO; + } + + p = (u32 *) iov[1].iov_base; + end = (u32 *) ((u8 *) p + iov[1].iov_len); + + if (p != res->buffer) { + printk("NFS: p != res->buffer in %s:%d!!!\n", + __FILE__, __LINE__); + return -errno_NFSERR_IO; + } + + string = (char *) res->buffer + res->bufsiz; + entry = (struct nfs_entry *) res->buffer; + for (nr = 0; *p++; nr++, entry++) { + entry->fileid = ntohl(*p++); + + len = ntohl(*p++); + if ((p + QUADLEN(len) + 3) > end) { + printk(KERN_NOTICE + "NFS: short packet in readdir reply!\n"); + break; + } + if (len > NFS_MAXNAMLEN) { + printk("NFS: giant filename in readdir (len %x)!\n", + len); + return -errno_NFSERR_IO; + } + string -= len; + if ((void *) (entry+1) > (void *) string) { + dprintk("NFS: shouldnothappen in readdirres_decode!\n"); + break; /* should not happen */ + } + + entry->name = string; + entry->length = len; + memmove(string, p, len); + p += QUADLEN(len); + entry->cookie = ntohl(*p++); + entry->eof = !p[0] && p[1]; + /* + dprintk("NFS: decoded dirent %.*s cookie %d eof %d\n", + len, string, entry->cookie, entry->eof); + */ + } + return nr; +} + +/* + * NFS XDR decode functions + */ +/* + * Decode void reply + */ +static int +nfs_xdr_dec_void(struct rpc_rqst *req, u32 *p, void *dummy) +{ + return 0; +} + +/* + * Decode simple status reply + */ +static int +nfs_xdr_stat(struct rpc_rqst *req, u32 *p, void *dummy) +{ + int status; + + if ((status = ntohl(*p++)) != 0) + status = -nfs_stat_to_errno(status); + return status; +} + +/* + * Decode attrstat reply + * GETATTR, SETATTR, WRITE + */ +static int +nfs_xdr_attrstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr) +{ + int status; + + dprintk("RPC: attrstat status %lx\n", ntohl(*p)); + if ((status = ntohl(*p++))) + return -nfs_stat_to_errno(status); + xdr_decode_fattr(p, fattr); + dprintk("RPC: attrstat OK type %d mode %o dev %x ino %x\n", + fattr->type, fattr->mode, fattr->fsid, fattr->fileid); + return 0; +} + +/* + * Decode diropres reply + * LOOKUP, CREATE, MKDIR + */ +static int +nfs_xdr_diropres(struct rpc_rqst *req, u32 *p, struct nfs_diropok *res) +{ + int status; + + dprintk("RPC: diropres status %lx\n", ntohl(*p)); + if ((status = ntohl(*p++))) + return -nfs_stat_to_errno(status); + p = xdr_decode_fhandle(p, res->fh); + xdr_decode_fattr(p, res->fattr); + dprintk("RPC: diropres OK type %x mode %o dev %x ino %x\n", + res->fattr->type, res->fattr->mode, + res->fattr->fsid, res->fattr->fileid); + return 0; +} + +/* + * Decode READLINK reply + */ +static int +nfs_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs_readlinkres *res) +{ + int status; + + if ((status = ntohl(*p++))) + return -nfs_stat_to_errno(status); + xdr_decode_string2(p, res->string, res->lenp, res->maxlen); + + /* Caller takes over the buffer here to avoid extra copy */ + res->buffer = req->rq_task->tk_buffer; + req->rq_task->tk_buffer = NULL; + return 0; +} + +/* + * Decode STATFS reply + */ +static int +nfs_xdr_statfsres(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res) +{ + int status; + + if ((status = ntohl(*p++))) + return -nfs_stat_to_errno(status); + res->tsize = ntohl(*p++); + res->bsize = ntohl(*p++); + res->blocks = ntohl(*p++); + res->bfree = ntohl(*p++); + res->bavail = ntohl(*p++); + return 0; +} + +/* + * We need to translate between nfs status return values and + * the local errno values which may not be the same. + */ +static struct { + int stat; + int errno; +} nfs_errtbl[] = { + { NFS_OK, 0 }, + { NFSERR_PERM, EPERM }, + { NFSERR_NOENT, ENOENT }, + { NFSERR_IO, errno_NFSERR_IO }, + { NFSERR_NXIO, ENXIO }, + { NFSERR_EAGAIN, EAGAIN }, + { NFSERR_ACCES, EACCES }, + { NFSERR_EXIST, EEXIST }, + { NFSERR_NODEV, ENODEV }, + { NFSERR_NOTDIR, ENOTDIR }, + { NFSERR_ISDIR, EISDIR }, + { NFSERR_INVAL, EINVAL }, + { NFSERR_FBIG, EFBIG }, + { NFSERR_NOSPC, ENOSPC }, + { NFSERR_ROFS, EROFS }, + { NFSERR_NAMETOOLONG, ENAMETOOLONG }, + { NFSERR_NOTEMPTY, ENOTEMPTY }, + { NFSERR_DQUOT, EDQUOT }, + { NFSERR_STALE, ESTALE }, +#ifdef EWFLUSH + { NFSERR_WFLUSH, EWFLUSH }, +#endif + { -1, EIO } +}; + +static int +nfs_stat_to_errno(int stat) +{ + int i; + + for (i = 0; nfs_errtbl[i].stat != -1; i++) { + if (nfs_errtbl[i].stat == stat) + return nfs_errtbl[i].errno; + } + printk("nfs_stat_to_errno: bad nfs status return value: %d\n", stat); + return nfs_errtbl[i].errno; +} + +#ifndef MAX +# define MAX(a, b) (((a) > (b))? (a) : (b)) +#endif + +#define PROC(proc, argtype, restype) \ + { "nfs_" #proc, \ + (kxdrproc_t) nfs_xdr_##argtype, \ + (kxdrproc_t) nfs_xdr_##restype, \ + MAX(NFS_##argtype##_sz,NFS_##restype##_sz) << 2 \ + } + +static struct rpc_procinfo nfs_procedures[18] = { + PROC(null, enc_void, dec_void), + PROC(getattr, fhandle, attrstat), + PROC(setattr, sattrargs, attrstat), + PROC(root, enc_void, dec_void), + PROC(lookup, diropargs, diropres), + PROC(readlink, fhandle, readlinkres), + PROC(read, readargs, readres), + PROC(writecache, enc_void, dec_void), + PROC(write, writeargs, attrstat), + PROC(create, createargs, diropres), + PROC(remove, diropargs, stat), + PROC(rename, renameargs, stat), + PROC(link, linkargs, stat), + PROC(symlink, symlinkargs, stat), + PROC(mkdir, createargs, diropres), + PROC(rmdir, diropargs, stat), + PROC(readdir, readdirargs, readdirres), + PROC(statfs, fhandle, statfsres), +}; + +static struct rpc_version nfs_version2 = { + 2, + sizeof(nfs_procedures)/sizeof(nfs_procedures[0]), + nfs_procedures +}; + +static struct rpc_version * nfs_version[] = { + NULL, + NULL, + &nfs_version2 +}; + +struct rpc_program nfs_program = { + "nfs", + NFS_PROGRAM, + sizeof(nfs_version) / sizeof(nfs_version[0]), + nfs_version, + &nfs_rpcstat, +}; + +/* + * RPC stats support + */ +static int +nfs_get_info(char *buffer, char **start, off_t offset, int length, int dummy) +{ + return rpcstat_get_info(&nfs_rpcstat, buffer, start, offset, length); +} + +static struct proc_dir_entry proc_nfsclnt = { + 0, 3, "nfs", + S_IFREG | S_IRUGO, 1, 0, 0, + 6, &proc_net_inode_operations, + nfs_get_info +}; + +struct rpc_stat nfs_rpcstat = { + NULL, /* next */ + &proc_nfsclnt, /* /proc/net directory entry */ + &nfs_program, /* RPC program */ +}; diff -u --recursive --new-file v2.1.31/linux/fs/nfs/nfsiod.c linux/fs/nfs/nfsiod.c --- v2.1.31/linux/fs/nfs/nfsiod.c Sat Jun 29 02:00:46 1996 +++ linux/fs/nfs/nfsiod.c Wed Dec 31 16:00:00 1969 @@ -1,120 +0,0 @@ -/* - * linux/fs/nfs/nfsiod.c - * - * Async NFS RPC call support. - * - * When a process wants to place an asynchronous RPC call, it reserves - * an nfsiod slot, fills in all necessary fields including the callback - * handler field, and enqueues the request. - * - * This will wake up nfsiod, which calls nfs_rpc_doio to collect the - * reply. It then dispatches the result to the caller via the callback - * function, including result value and request pointer. It then re-inserts - * itself into the free list. - * - * Copyright (C) 1996, Olaf Kirch - */ - -#include -#include -#include -#include -#include -#include - -static struct nfsiod_req * free_list = NULL; -static int active = 0; - -#undef DEBUG_NFSIOD -#ifdef DEBUG_NFSIOD -#define dprintk(args...) printk(## args) -#else -#define dprintk(args...) /* nothing */ -#endif - - -/* - * Reserve an nfsiod slot and initialize the request struct - */ -struct nfsiod_req * -nfsiod_reserve(struct nfs_server *server) -{ - struct nfsiod_req *req; - - if (!(req = free_list)) { - dprintk("BIO: nfsiod_reserve: no free nfsiods\n"); - return NULL; - } - free_list = req->rq_next; - memset(&req->rq_rpcreq, 0, sizeof(struct rpc_ioreq)); - - if (rpc_reserve(server->rsock, &req->rq_rpcreq, 1) < 0) { - dprintk("BIO: nfsiod_reserve failed to reserve RPC slot\n"); - req->rq_next = free_list; - free_list = req; - return NULL; - } - - req->rq_server = server; - return req; -} - -void -nfsiod_release(struct nfsiod_req *req) -{ - dprintk("BIO: nfsiod_release called\n"); - rpc_release(req->rq_server->rsock, &req->rq_rpcreq); - memset(&req->rq_rpcreq, 0, sizeof(struct rpc_ioreq)); - req->rq_next = free_list; - free_list = req; -} - -/* - * Transmit a request and put it on nfsiod's list of pending requests. - */ -void -nfsiod_enqueue(struct nfsiod_req *req) -{ - dprintk("BIO: enqueuing request %p\n", &req->rq_rpcreq); - wake_up(&req->rq_wait); - schedule(); -} - -/* - * This is the main nfsiod loop. - */ -int -nfsiod(void) -{ - struct nfsiod_req request, *req = &request; - int result; - - dprintk("BIO: nfsiod %d starting\n", current->pid); - while (1) { - /* Insert request into free list */ - memset(req, 0, sizeof(*req)); - req->rq_next = free_list; - free_list = req; - - /* Wait until user enqueues request */ - dprintk("BIO: before: now %d nfsiod's active\n", active); - dprintk("BIO: nfsiod %d waiting\n", current->pid); - interruptible_sleep_on(&req->rq_wait); - - if (current->signal & ~current->blocked) - break; - if (!req->rq_rpcreq.rq_slot) - continue; - dprintk("BIO: nfsiod %d woken up; calling nfs_rpc_doio.\n", - current->pid); - active++; - dprintk("BIO: before: now %d nfsiod's active\n", active); - do { - result = nfs_rpc_doio(req->rq_server, - &req->rq_rpcreq, 1); - } while (!req->rq_callback(result, req)); - active--; - } - - return 0; -} diff -u --recursive --new-file v2.1.31/linux/fs/nfs/nfsroot.c linux/fs/nfs/nfsroot.c --- v2.1.31/linux/fs/nfs/nfsroot.c Thu Feb 27 10:57:31 1997 +++ linux/fs/nfs/nfsroot.c Fri Apr 4 11:05:59 1997 @@ -51,8 +51,6 @@ * without giving a path name. Fix BOOTP request * for domainname (domainname is NIS domain, not * DNS domain!). Skip dummy devices for BOOTP. - * Jacek Zapala : Fixed a bug which prevented server-ip address - * from nfsroot parameter from being used. * */ @@ -79,22 +77,20 @@ #include #include #include -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) +#ifdef CONFIG_AX25 #include /* For AX25_P_IP */ #endif #include #include #include -#include #include #include #include #include -#include #include #include -#include +#include /* Range of privileged ports */ #define STARTPORT 600 @@ -154,6 +150,8 @@ /* Yes, we use sys_socket, but there's no include file for it */ extern asmlinkage int sys_socket(int family, int type, int protocol); + + /*************************************************************************** Device Handling Subroutines @@ -172,20 +170,18 @@ last = &open_base; for (dev = dev_base; dev != NULL; dev = dev->next) { - if ( -#if !CONFIG_AP1000 - dev->type < ARPHRD_SLIP && -#endif + if (dev->type < ARPHRD_SLIP && dev->family == AF_INET && !(dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) && (0 != strncmp(dev->name, "dummy", 5)) && (!user_dev_name[0] || !strcmp(dev->name, user_dev_name))) { - struct ifreq ifr; + /* First up the interface */ old_flags = dev->flags; - strncpy(ifr.ifr_name, dev->name, IFNAMSIZ); - ifr.ifr_flags = old_flags | IFF_UP | IFF_RUNNING; - if (dev_ioctl(SIOCSIFFLAGS, &ifr) < 0) + dev->flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING; + if (!(old_flags & IFF_UP) && dev_open(dev)) { + dev->flags = old_flags; continue; + } openp = (struct open_dev *) kmalloc(sizeof(struct open_dev), GFP_ATOMIC); if (openp == NULL) @@ -226,10 +222,9 @@ nextp = openp->next; openp->next = NULL; if (openp->dev != root_dev) { - struct ifreq ifr; - strncpy(ifr.ifr_name, openp->dev->name, IFNAMSIZ); - ifr.ifr_flags = openp->old_flags; - dev_ioctl(SIOCSIFFLAGS, &ifr); + if (!(openp->old_flags & IFF_UP)) + dev_close(openp->dev); + openp->dev->flags = openp->old_flags; } kfree_s(openp, sizeof(struct open_dev)); openp = nextp; @@ -295,7 +290,7 @@ unsigned long sip, tip; unsigned char *sha, *tha; /* s for "source", t for "target" */ - /* If this test doesn't pass, it's not IP, or we should ignore it anyway */ + /* If this test doesn't pass, its not IP, or we should ignore it anyway */ if (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd)) { kfree_skb(skb, FREE_READ); return 0; @@ -309,7 +304,7 @@ /* If it's not ethernet or AX25, delete it. */ if ((rarp->ar_pro != htons(ETH_P_IP) && dev->type != ARPHRD_AX25) || -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) +#ifdef CONFIG_AX25 (rarp->ar_pro != htons(AX25_P_IP) && dev->type == ARPHRD_AX25) || #endif rarp->ar_pln != 4) { @@ -465,23 +460,17 @@ */ static int root_add_bootp_route(void) { - struct { - struct nlmsghdr nlh; - struct in_rtmsg rtm; - } rtreq; - int err; - unsigned short fs; - - memset(&rtreq, 0, sizeof(rtreq)); - rtreq.nlh.nlmsg_type = RTMSG_NEWROUTE; - rtreq.nlh.nlmsg_len = sizeof(rtreq); - rtreq.rtm.rtmsg_ifindex = bootp_dev->ifindex; - rtreq.rtm.rtmsg_flags = RTF_UP; - fs = get_fs(); - set_fs(get_ds()); - err = ip_rt_ioctl(SIOCRTMSG, &rtreq); - set_fs(fs); - if (err) { + struct rtentry route; + + memset(&route, 0, sizeof(route)); + route.rt_dev = bootp_dev->name; + route.rt_mss = bootp_dev->mtu; + route.rt_flags = RTF_UP; + ((struct sockaddr_in *) &(route.rt_dst)) -> sin_addr.s_addr = 0; + ((struct sockaddr_in *) &(route.rt_dst)) -> sin_family = AF_INET; + ((struct sockaddr_in *) &(route.rt_genmask)) -> sin_addr.s_addr = 0; + ((struct sockaddr_in *) &(route.rt_genmask)) -> sin_family = AF_INET; + if (ip_rt_new(&route)) { printk(KERN_ERR "BOOTP: Adding of route failed!\n"); return -1; } @@ -495,25 +484,14 @@ */ static int root_del_bootp_route(void) { - struct { - struct nlmsghdr nlh; - struct in_rtmsg rtm; - } rtreq; - int err; - unsigned short fs; + struct rtentry route; if (!bootp_have_route) return 0; - memset(&rtreq, 0, sizeof(rtreq)); - rtreq.nlh.nlmsg_type = RTMSG_DELROUTE; - rtreq.nlh.nlmsg_len = sizeof(rtreq); - rtreq.rtm.rtmsg_ifindex = bootp_dev->ifindex; - rtreq.rtm.rtmsg_flags = RTF_UP; - fs = get_fs(); - set_fs(get_ds()); - err = ip_rt_ioctl(SIOCRTMSG, &rtreq); - set_fs(fs); - if (err) { + memset(&route, 0, sizeof(route)); + ((struct sockaddr_in *) &(route.rt_dst)) -> sin_addr.s_addr = 0; + ((struct sockaddr_in *) &(route.rt_genmask)) -> sin_addr.s_addr = 0; + if (ip_rt_kill(&route)) { printk(KERN_ERR "BOOTP: Deleting of route failed!\n"); return -1; } @@ -598,13 +576,10 @@ iov.iov_base = buf; iov.iov_len = size; msg.msg_name = NULL; - msg.msg_namelen = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; - msg.msg_control = NULL; - msg.msg_controllen = 0; - msg.msg_flags = 0; - result = sock_sendmsg(sock, &msg, size); + msg.msg_accrights = NULL; + result = sock->ops->sendmsg(sock, &msg, size, 0, 0); set_fs(oldfs); return (result != size); } @@ -625,13 +600,11 @@ iov.iov_base = buf; iov.iov_len = size; msg.msg_name = NULL; - msg.msg_namelen = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; - msg.msg_control = NULL; - msg.msg_controllen = 0; + msg.msg_accrights = NULL; msg.msg_namelen = 0; - result = sock_recvmsg(sock, &msg, size, MSG_DONTWAIT); + result = sock->ops->recvmsg(sock, &msg, size, O_NONBLOCK, 0, &msg.msg_namelen); set_fs(oldfs); return result; } @@ -746,9 +719,9 @@ return -1; /* Bind/connect the sockets */ - bootp_xmit_sock->sk->broadcast = 1; - bootp_xmit_sock->sk->reuse = 1; - bootp_recv_sock->sk->reuse = 1; + ((struct sock *) bootp_xmit_sock->data) -> broadcast = 1; + ((struct sock *) bootp_xmit_sock->data) -> reuse = 1; + ((struct sock *) bootp_recv_sock->data) -> reuse = 1; if (root_bind_udp_sock(bootp_recv_sock, INADDR_ANY, 68) || root_bind_udp_sock(bootp_xmit_sock, INADDR_ANY, 68) || root_connect_udp_sock(bootp_xmit_sock, INADDR_BROADCAST, 67)) @@ -788,9 +761,9 @@ */ static void root_do_bootp_ext(u8 *ext) { -#ifdef NFSROOT_BOOTP_DEBUG u8 *c; +#ifdef NFSROOT_BOOTP_DEBUG printk("BOOTP: Got extension %02x",*ext); for(c=ext+2; cifindex; - rtreq.rtm.rtmsg_flags = RTF_UP|RTF_GATEWAY; - rtreq.rtm.rtmsg_gateway = gw; - fs = get_fs(); - set_fs(get_ds()); - err = ip_rt_ioctl(SIOCRTMSG, &rtreq); - set_fs(fs); - return err; } + /* * Set the interface address and configure a route to the server. */ static int root_nfs_setup(void) { - unsigned short fs; - struct ifreq ifr; - int err; + struct rtentry route; /* Set the default system name in case none was previously found */ if (!system_utsname.nodename[0]) { @@ -1337,22 +1281,16 @@ system_utsname.nodename[__NEW_UTS_LEN] = '\0'; } - fs = get_fs(); - set_fs(get_ds()); - strncpy(ifr.ifr_name, root_dev->name, IFNAMSIZ); - memcpy(&ifr.ifr_addr, &myaddr, sizeof(myaddr)); - devinet_ioctl(SIOCSIFADDR, &ifr); - if (netmask.sin_addr.s_addr != INADDR_NONE && - netmask.sin_addr.s_addr != root_dev->pa_mask) { - memcpy(&ifr.ifr_netmask, &netmask, sizeof(netmask)); - devinet_ioctl(SIOCSIFNETMASK, &ifr); - memcpy(&ifr.ifr_broadaddr, &netmask, sizeof(netmask)); - ((struct sockaddr_in*)&ifr.ifr_broadaddr)->sin_addr.s_addr = - root_dev->pa_addr | ~root_dev->pa_mask; - devinet_ioctl(SIOCSIFBRDADDR, &ifr); - } - netmask.sin_addr.s_addr = root_dev->pa_mask; - set_fs(fs); + /* Set the correct netmask */ + if (netmask.sin_addr.s_addr == INADDR_NONE) + netmask.sin_addr.s_addr = ip_get_mask(myaddr.sin_addr.s_addr); + + /* Setup the device correctly */ + root_dev->family = myaddr.sin_family; + root_dev->pa_addr = myaddr.sin_addr.s_addr; + root_dev->pa_mask = netmask.sin_addr.s_addr; + root_dev->pa_brdaddr = root_dev->pa_addr | ~root_dev->pa_mask; + root_dev->pa_dstaddr = 0; /* * Now add a route to the server. If there is no gateway given, @@ -1362,10 +1300,28 @@ * gatewayed default route. Note that this gives sufficient network * setup even for full system operation in all common cases. */ + memset(&route, 0, sizeof(route)); /* Local subnet route */ + route.rt_dev = root_dev->name; + route.rt_mss = root_dev->mtu; + route.rt_flags = RTF_UP; + *((struct sockaddr_in *) &(route.rt_dst)) = myaddr; + (((struct sockaddr_in *) &(route.rt_dst)))->sin_addr.s_addr &= netmask.sin_addr.s_addr; + *((struct sockaddr_in *) &(route.rt_genmask)) = netmask; + if (ip_rt_new(&route)) { + printk(KERN_ERR "Root-NFS: Adding of local route failed!\n"); + return -1; + } if (gateway.sin_addr.s_addr != INADDR_NONE) { /* Default route */ - err = root_nfs_add_default_route(gateway.sin_addr, root_dev); - if (err) { + (((struct sockaddr_in *) &(route.rt_dst)))->sin_addr.s_addr = INADDR_ANY; + (((struct sockaddr_in *) &(route.rt_genmask)))->sin_addr.s_addr = INADDR_ANY; + *((struct sockaddr_in *) &(route.rt_gateway)) = gateway; + route.rt_flags |= RTF_GATEWAY; + if ((gateway.sin_addr.s_addr ^ myaddr.sin_addr.s_addr) & netmask.sin_addr.s_addr) { + printk(KERN_ERR "Root-NFS: Gateway not on local network!\n"); + return -1; + } + if (ip_rt_new(&route)) { printk(KERN_ERR "Root-NFS: Adding of default route failed!\n"); return -1; } @@ -1600,7 +1556,7 @@ */ static int *root_nfs_header(int proc, int program, int version) { - gid_t groups[] = { 0 }; + int groups[] = { 0, NOGROUP }; if (rpc_packet == NULL) { if (!(rpc_packet = kmalloc(nfs_data.wsize + 1024, GFP_NFS))) { @@ -1608,7 +1564,7 @@ return NULL; } } - return rpc_header(rpc_packet, proc, program, version, 0, 0, 1, groups); + return rpc_header(rpc_packet, proc, program, version, 0, 0, groups); } diff -u --recursive --new-file v2.1.31/linux/fs/nfs/proc.c linux/fs/nfs/proc.c --- v2.1.31/linux/fs/nfs/proc.c Fri Nov 22 02:33:29 1996 +++ linux/fs/nfs/proc.c Fri Apr 4 11:05:59 1997 @@ -21,1017 +21,272 @@ * it decodes the packet. * * Feel free to fix it and mail me the diffs if it worries you. + * + * Completely rewritten to support the new RPC call interface; + * rewrote and moved the entire XDR stuff to xdr.c + * --Olaf Kirch June 1996 */ -/* - * Defining NFS_PROC_DEBUG causes a lookup of a file named - * "xyzzy" to toggle debugging. Just cd to an NFS-mounted - * filesystem and type 'ls xyzzy' to turn on debugging. - */ - -#if 0 -#define NFS_PROC_DEBUG -#endif +#define NFS_NEED_XDR_TYPES #include #include #include #include -#include #include #include #include #include #include +#include +#include -#include - -#ifdef NFS_PROC_DEBUG - -static int proc_debug = 0; -#define PRINTK(format, args...) \ - do { \ - if (proc_debug) \ - printk(format , ## args); \ - } while (0) - -#else /* !NFS_PROC_DEBUG */ - -#define PRINTK(format, args...) do ; while (0) - -#endif /* !NFS_PROC_DEBUG */ - -/* Mapping from NFS error code to "errno" error code. */ -#define errno_NFSERR_IO EIO - -static int *nfs_rpc_header(int *p, int procedure, int ruid); -static int *nfs_rpc_verify(int *p); -static int nfs_stat_to_errno(int stat); - -/* - * Our memory allocation and release functions. - */ - -#define NFS_SLACK_SPACE 1024 /* Total overkill */ -/* !!! Be careful, this constant is now also used in sock.c... - We should easily convert to not using it anymore for most cases... */ - -static inline int *nfs_rpc_alloc(int size) -{ - int *i; - - while (!(i = (int *)kmalloc(size+NFS_SLACK_SPACE,GFP_NFS))) { - schedule(); - } - return i; -} - -static inline void nfs_rpc_free(int *p) -{ - kfree((void *)p); -} +#include /* - * Here are a bunch of xdr encode/decode functions that convert - * between machine dependent and xdr data formats. + * If NFS_DEBUG is defined, you can toggle NFS debugging by causing + * a lookup of "xyzzy". Just cd to an NFS-mounted filesystem and type + * 'ls xyzzy' to turn on debugging. */ +#ifdef NFS_DEBUG +# define NFSDBG_FACILITY NFSDBG_PROC +#endif -#define QUADLEN(len) (((len) + 3) >> 2) - -static inline int *xdr_encode_fhandle(int *p, struct nfs_fh *fhandle) -{ - *((struct nfs_fh *) p) = *fhandle; - return p + QUADLEN(sizeof(*fhandle)); -} - -static inline int *xdr_decode_fhandle(int *p, struct nfs_fh *fhandle) -{ - *fhandle = *((struct nfs_fh *) p); - return p + QUADLEN(sizeof(*fhandle)); -} - -static inline int *xdr_encode_string(int *p, const char *string) -{ - int len = strlen(string); - int quadlen = QUADLEN(len); - - p[quadlen] = 0; - *p++ = htonl(len); - memcpy(p, string, len); - return p + quadlen; -} - -static inline int *xdr_decode_string(int *p, char *string, unsigned int maxlen) -{ - unsigned int len = ntohl(*p++); - if (len > maxlen) - return NULL; - memcpy(string, p, len); - string[len] = '\0'; - return p + QUADLEN(len); -} - -static inline int *xdr_decode_string2(int *p, char **string, unsigned int *len, - unsigned int maxlen) -{ - *len = ntohl(*p++); - if (*len > maxlen) - return NULL; - *string = (char *) p; - return p + QUADLEN(*len); -} - - -static inline int *xdr_encode_data(int *p, const char *data, int len) -{ - int quadlen = QUADLEN(len); - - p[quadlen] = 0; - *p++ = htonl(len); - copy_from_user(p, data, len); - return p + quadlen; -} - -static inline int *xdr_decode_data(int *p, char *data, int *lenp, int maxlen) -{ - unsigned len = *lenp = ntohl(*p++); - if (len > maxlen) - return NULL; - memcpy(data, p, len); - return p + QUADLEN(len); -} - -static int *xdr_decode_fattr(int *p, struct nfs_fattr *fattr) -{ - fattr->type = (enum nfs_ftype) ntohl(*p++); - fattr->mode = ntohl(*p++); - fattr->nlink = ntohl(*p++); - fattr->uid = ntohl(*p++); - fattr->gid = ntohl(*p++); - fattr->size = ntohl(*p++); - fattr->blocksize = ntohl(*p++); - fattr->rdev = ntohl(*p++); - fattr->blocks = ntohl(*p++); - fattr->fsid = ntohl(*p++); - fattr->fileid = ntohl(*p++); - fattr->atime.seconds = ntohl(*p++); - fattr->atime.useconds = ntohl(*p++); - fattr->mtime.seconds = ntohl(*p++); - fattr->mtime.useconds = ntohl(*p++); - fattr->ctime.seconds = ntohl(*p++); - fattr->ctime.useconds = ntohl(*p++); - return p; -} - -static int *xdr_encode_sattr(int *p, struct nfs_sattr *sattr) -{ - *p++ = htonl(sattr->mode); - *p++ = htonl(sattr->uid); - *p++ = htonl(sattr->gid); - *p++ = htonl(sattr->size); - *p++ = htonl(sattr->atime.seconds); - *p++ = htonl(sattr->atime.useconds); - *p++ = htonl(sattr->mtime.seconds); - *p++ = htonl(sattr->mtime.useconds); - return p; -} - -static int *xdr_decode_entry(int *p, struct nfs_entry *entry) -{ - entry->fileid = ntohl(*p++); - if (!(p = xdr_decode_string(p, entry->name, NFS_MAXNAMLEN))) - return NULL; - entry->cookie = ntohl(*p++); - entry->eof = 0; - return p; -} - -static int *xdr_decode_fsinfo(int *p, struct nfs_fsinfo *res) -{ - res->tsize = ntohl(*p++); - res->bsize = ntohl(*p++); - res->blocks = ntohl(*p++); - res->bfree = ntohl(*p++); - res->bavail = ntohl(*p++); - return p; -} /* * One function for each procedure in the NFS protocol. */ - -int nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fattr *fattr) +int +nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, + struct nfs_fattr *fattr) { - int *p, *p0; - int status; - int ruid = 0; - - PRINTK("NFS call getattr\n"); - if (!(p0 = nfs_rpc_alloc(server->rsize))) - return -EIO; -retry: - p = nfs_rpc_header(p0, NFSPROC_GETATTR, ruid); - p = xdr_encode_fhandle(p, fhandle); - if ((status = nfs_rpc_call(server, p0, p, server->rsize)) < 0) { - nfs_rpc_free(p0); - return status; - } - if (!(p = nfs_rpc_verify(p0))) - status = -errno_NFSERR_IO; - else if ((status = ntohl(*p++)) == NFS_OK) { - p = xdr_decode_fattr(p, fattr); - PRINTK("NFS reply getattr\n"); - /* status = 0; */ - } - else { - if (!ruid && current->fsuid == 0 && current->uid != 0) { - ruid = 1; - goto retry; - } - PRINTK("NFS reply getattr failed = %d\n", status); - status = -nfs_stat_to_errno(status); - } - nfs_rpc_free(p0); + int status; + + dprintk("NFS call getattr\n"); + status = rpc_call(server->client, NFSPROC_GETATTR, fhandle, fattr, 0); + dprintk("NFS reply getattr\n"); return status; } -int nfs_proc_setattr(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_sattr *sattr, struct nfs_fattr *fattr) +int +nfs_proc_setattr(struct nfs_server *server, struct nfs_fh *fhandle, + struct nfs_sattr *sattr, struct nfs_fattr *fattr) { - int *p, *p0; - int status; - int ruid = 0; - - PRINTK("NFS call setattr\n"); - if (!(p0 = nfs_rpc_alloc(server->wsize))) - return -EIO; -retry: - p = nfs_rpc_header(p0, NFSPROC_SETATTR, ruid); - p = xdr_encode_fhandle(p, fhandle); - p = xdr_encode_sattr(p, sattr); - if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) { - nfs_rpc_free(p0); - return status; - } - if (!(p = nfs_rpc_verify(p0))) - status = -errno_NFSERR_IO; - else if ((status = ntohl(*p++)) == NFS_OK) { - p = xdr_decode_fattr(p, fattr); - PRINTK("NFS reply setattr\n"); - /* status = 0; */ - } - else { - if (!ruid && current->fsuid == 0 && current->uid != 0) { - ruid = 1; - goto retry; - } - PRINTK("NFS reply setattr failed = %d\n", status); - status = -nfs_stat_to_errno(status); - } - nfs_rpc_free(p0); + struct nfs_sattrargs arg = { fhandle, sattr }; + int status; + + dprintk("NFS call setattr\n"); + status = rpc_call(server->client, NFSPROC_SETATTR, &arg, fattr, 0); + dprintk("NFS reply setattr\n"); return status; } -int nfs_proc_lookup(struct nfs_server *server, struct nfs_fh *dir, const char *name, +int +nfs_proc_lookup(struct nfs_server *server, struct nfs_fh *dir, const char *name, struct nfs_fh *fhandle, struct nfs_fattr *fattr) { - int *p, *p0; - int status; - int ruid = 0; + struct nfs_diropargs arg = { dir, name }; + struct nfs_diropok res = { fhandle, fattr }; + int status; - PRINTK("NFS call lookup %s\n", name); -#ifdef NFS_PROC_DEBUG + dprintk("NFS call lookup %s\n", name); +#ifdef RPC_DEBUG if (!strcmp(name, "xyzzy")) - proc_debug = 1 - proc_debug; + nfs_debug = ~nfs_debug; #endif - if (!(p0 = nfs_rpc_alloc(server->rsize))) - return -EIO; -retry: - p = nfs_rpc_header(p0, NFSPROC_LOOKUP, ruid); - p = xdr_encode_fhandle(p, dir); - p = xdr_encode_string(p, name); - if ((status = nfs_rpc_call(server, p0, p, server->rsize)) < 0) { - nfs_rpc_free(p0); - return status; - } - if (!(p = nfs_rpc_verify(p0))) - status = -errno_NFSERR_IO; - else if ((status = ntohl(*p++)) == NFS_OK) { - p = xdr_decode_fhandle(p, fhandle); - p = xdr_decode_fattr(p, fattr); - PRINTK("NFS reply lookup\n"); - /* status = 0; */ - } - else { - if (!ruid && current->fsuid == 0 && current->uid != 0) { - ruid = 1; - goto retry; - } - PRINTK("NFS reply lookup failed = %d\n", status); - status = -nfs_stat_to_errno(status); - } - nfs_rpc_free(p0); + status = rpc_call(server->client, NFSPROC_LOOKUP, &arg, &res, 0); + dprintk("NFS reply lookup: %d\n", status); return status; } -int nfs_proc_readlink(struct nfs_server *server, struct nfs_fh *fhandle, - int **p0, char **string, unsigned int *len, unsigned int maxlen) +int +nfs_proc_readlink(struct nfs_server *server, struct nfs_fh *fhandle, + void **p0, char **string, unsigned int *len, + unsigned int maxlen) { - int *p; - int status, ruid = 0; - - PRINTK("NFS call readlink\n"); - if (!(*p0 = nfs_rpc_alloc(server->rsize))) - return -EIO; -retry: - p = nfs_rpc_header(*p0, NFSPROC_READLINK, ruid); - p = xdr_encode_fhandle(p, fhandle); - if ((status = nfs_rpc_call(server, *p0, p, server->rsize)) < 0) - return status; - if (!(p = nfs_rpc_verify(*p0))) - status = -errno_NFSERR_IO; - else if ((status = ntohl(*p++)) == NFS_OK) { - if (!(p = xdr_decode_string2(p, string, len, maxlen))) { - printk("nfs_proc_readlink: giant pathname\n"); - status = -errno_NFSERR_IO; - } - else /* status = 0, */ - PRINTK("NFS reply readlink\n"); - } - else { - if (!ruid && current->fsuid == 0 && current->uid != 0) { - ruid = 1; - goto retry; - } - PRINTK("NFS reply readlink failed = %d\n", status); - status = -nfs_stat_to_errno(status); - } - return status; -} + struct nfs_readlinkres res = { string, len, maxlen, NULL }; + int status; -int nfs_proc_read(struct nfs_server *server, struct nfs_fh *fhandle, - int offset, int count, char *data, struct nfs_fattr *fattr) -{ - int *p, *p0; - int status; - int ruid = 0; - int len; - - PRINTK("NFS call read %d @ %d\n", count, offset); - if (!(p0 = nfs_rpc_alloc(server->rsize))) - return -EIO; -retry: - p = nfs_rpc_header(p0, NFSPROC_READ, ruid); - p = xdr_encode_fhandle(p, fhandle); - *p++ = htonl(offset); - *p++ = htonl(count); - *p++ = htonl(count); /* traditional, could be any value */ - if ((status = nfs_rpc_call(server, p0, p, server->rsize)) < 0) { - nfs_rpc_free(p0); - return status; - } - if (!(p = nfs_rpc_verify(p0))) - status = -errno_NFSERR_IO; - else if ((status = ntohl(*p++)) == NFS_OK) { - p = xdr_decode_fattr(p, fattr); - if (!(p = xdr_decode_data(p, data, &len, count))) { - printk("nfs_proc_read: giant data size\n"); - status = -errno_NFSERR_IO; - } - else { - status = len; - PRINTK("NFS reply read %d\n", len); - } - } - else { - if (!ruid && current->fsuid == 0 && current->uid != 0) { - ruid = 1; - goto retry; - } - PRINTK("NFS reply read failed = %d\n", status); - status = -nfs_stat_to_errno(status); - } - nfs_rpc_free(p0); + dprintk("NFS call readlink\n"); + status = rpc_call(server->client, NFSPROC_READLINK, fhandle, &res, 0); + dprintk("NFS reply readlink: %d\n", status); + if (!status) + *p0 = res.buffer; + else if (res.buffer) + kfree(res.buffer); return status; } int -nfs_proc_read_request(struct rpc_ioreq *req, struct nfs_server *server, - struct nfs_fh *fh, unsigned long offset, - unsigned long count, __u32 *buf) -{ - __u32 *p, *p0; - int len; - - PRINTK("NFS reqst read %ld @ %ld\n", count, offset); - if (!(p0 = nfs_rpc_alloc(NFS_SLACK_SPACE))) - return -EIO; - - p = nfs_rpc_header(p0, NFSPROC_READ, 0); - p = xdr_encode_fhandle(p, fh); - *p++ = htonl(offset); - *p++ = htonl(count); - *p++ = htonl(count); /* traditional, could be any value */ - req->rq_svec[0].iov_base = p0; - req->rq_svec[0].iov_len = (p - p0) << 2; - req->rq_slen = (p - p0) << 2; - req->rq_snr = 1; - - len = (6 + 1 + 17 + 1); /* standard READ reply header */ - req->rq_rvec[0].iov_base = p0; - req->rq_rvec[0].iov_len = len << 2; - req->rq_rvec[1].iov_base = buf; - req->rq_rvec[1].iov_len = count; - req->rq_rvec[2].iov_base = p0 + len; /* spill buffer */ - req->rq_rvec[2].iov_len = (NFS_SLACK_SPACE - len) << 2; - req->rq_rlen = count + NFS_SLACK_SPACE; - req->rq_rnr = 3; - - req->rq_addr = &server->toaddr; - req->rq_alen = sizeof(server->toaddr); - - return rpc_transmit(server->rsock, req); -} - -int -nfs_proc_read_reply(struct rpc_ioreq *req, struct nfs_fattr *fattr) -{ - int status; - __u32 *p0, *p; - int count; - - p0 = (__u32 *) req->rq_rvec[0].iov_base; - - if (!(p = nfs_rpc_verify(p0))) { - /* Tell the upper layers to retry */ - status = -EAGAIN; - /* status = -errno_NFSERR_IO; */ - } else if ((status = ntohl(*p++)) == NFS_OK) { - p = xdr_decode_fattr(p, fattr); - count = ntohl(*p++); - if (p != req->rq_rvec[2].iov_base) { - /* unexpected RPC reply header size. punt. - * fixme: move iovec contents to align data - * on page boundary and adjust RPC header size - * guess. */ - status = -errno_NFSERR_IO; - PRINTK("NFS reply read odd header size %d\n", - (p - p0) << 2); - } else { - status = count; - PRINTK("NFS reply read %d\n", count); - } - } - else { - PRINTK("NFS reply read failed = %d\n", status); - status = -nfs_stat_to_errno(status); - } - nfs_rpc_free(p0); +nfs_proc_read(struct nfs_server *server, struct nfs_fh *fhandle, int swap, + unsigned long offset, unsigned int count, + void *buffer, struct nfs_fattr *fattr) +{ + struct nfs_readargs arg = { fhandle, offset, count, buffer }; + struct nfs_readres res = { fattr, count }; + int status; + + dprintk("NFS call read %d @ %ld\n", count, offset); + status = rpc_call(server->client, NFSPROC_READ, &arg, &res, + swap? NFS_RPC_SWAPFLAGS : 0); + dprintk("NFS reply read: %d\n", status); return status; } -int nfs_proc_write(struct inode * inode, int offset, - int count, const char *data, struct nfs_fattr *fattr) -{ - int *p, *p0; - int status; - int ruid = 0; - void * kdata; /* address of kernel copy */ - struct nfs_server * server = NFS_SERVER(inode); - struct nfs_fh *fhandle = NFS_FH(inode); - - PRINTK("NFS call write %d @ %d\n", count, offset); - if (!(p0 = nfs_rpc_alloc(server->wsize))) - return -EIO; -retry: - p = nfs_rpc_header(p0, NFSPROC_WRITE, ruid); - p = xdr_encode_fhandle(p, fhandle); - *p++ = htonl(offset); /* traditional, could be any value */ - *p++ = htonl(offset); - *p++ = htonl(count); /* traditional, could be any value */ - kdata = (void *) (p+1); /* start of data in RPC buffer */ - p = xdr_encode_data(p, data, count); - if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) { - nfs_rpc_free(p0); - return status; - } - if (!(p = nfs_rpc_verify(p0))) - status = -errno_NFSERR_IO; - else if ((status = ntohl(*p++)) == NFS_OK) { - update_vm_cache(inode, offset, kdata, count); - p = xdr_decode_fattr(p, fattr); - PRINTK("NFS reply write\n"); - /* status = 0; */ - } - else { - if (!ruid && current->fsuid == 0 && current->uid != 0) { - ruid = 1; - goto retry; - } - PRINTK("NFS reply write failed = %d\n", status); - status = -nfs_stat_to_errno(status); - } - nfs_rpc_free(p0); - return status; +int +nfs_proc_write(struct nfs_server *server, struct nfs_fh *fhandle, int swap, + unsigned long offset, unsigned int count, + const void *buffer, struct nfs_fattr *fattr) +{ + struct nfs_writeargs arg = { fhandle, offset, count, buffer }; + int status; + + dprintk("NFS call write %d @ %ld\n", count, offset); + status = rpc_call(server->client, NFSPROC_WRITE, &arg, fattr, + swap? (RPC_TASK_SWAPPER|RPC_TASK_ROOTCREDS) : 0); + dprintk("NFS reply read: %d\n", status); + return status < 0? status : count; } -int nfs_proc_create(struct nfs_server *server, struct nfs_fh *dir, - const char *name, struct nfs_sattr *sattr, - struct nfs_fh *fhandle, struct nfs_fattr *fattr) -{ - int *p, *p0; - int status; - int ruid = 0; - - PRINTK("NFS call create %s\n", name); - if (!(p0 = nfs_rpc_alloc(server->wsize))) - return -EIO; -retry: - p = nfs_rpc_header(p0, NFSPROC_CREATE, ruid); - p = xdr_encode_fhandle(p, dir); - p = xdr_encode_string(p, name); - p = xdr_encode_sattr(p, sattr); - if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) { - nfs_rpc_free(p0); - return status; - } - if (!(p = nfs_rpc_verify(p0))) - status = -errno_NFSERR_IO; - else if ((status = ntohl(*p++)) == NFS_OK) { - p = xdr_decode_fhandle(p, fhandle); - p = xdr_decode_fattr(p, fattr); - PRINTK("NFS reply create\n"); - /* status = 0; */ - } - else { - if (!ruid && current->fsuid == 0 && current->uid != 0) { - ruid = 1; - goto retry; - } - PRINTK("NFS reply create failed = %d\n", status); - status = -nfs_stat_to_errno(status); - } - nfs_rpc_free(p0); +int +nfs_proc_create(struct nfs_server *server, struct nfs_fh *dir, + const char *name, struct nfs_sattr *sattr, + struct nfs_fh *fhandle, struct nfs_fattr *fattr) +{ + struct nfs_createargs arg = { dir, name, sattr }; + struct nfs_diropok res = { fhandle, fattr }; + int status; + + dprintk("NFS call create %s\n", name); + status = rpc_call(server->client, NFSPROC_CREATE, &arg, &res, 0); + dprintk("NFS reply create: %d\n", status); return status; } -int nfs_proc_remove(struct nfs_server *server, struct nfs_fh *dir, const char *name) +int +nfs_proc_remove(struct nfs_server *server, struct nfs_fh *dir, const char *name) { - int *p, *p0; - int status; - int ruid = 0; - - PRINTK("NFS call remove %s\n", name); - if (!(p0 = nfs_rpc_alloc(server->wsize))) - return -EIO; -retry: - p = nfs_rpc_header(p0, NFSPROC_REMOVE, ruid); - p = xdr_encode_fhandle(p, dir); - p = xdr_encode_string(p, name); - if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) { - nfs_rpc_free(p0); - return status; - } - if (!(p = nfs_rpc_verify(p0))) - status = -errno_NFSERR_IO; - else if ((status = ntohl(*p++)) == NFS_OK) { - PRINTK("NFS reply remove\n"); - /* status = 0; */ - } - else { - if (!ruid && current->fsuid == 0 && current->uid != 0) { - ruid = 1; - goto retry; - } - PRINTK("NFS reply remove failed = %d\n", status); - status = -nfs_stat_to_errno(status); - } - nfs_rpc_free(p0); - return status; -} + struct nfs_diropargs arg = { dir, name }; + int status; -int nfs_proc_rename(struct nfs_server *server, - struct nfs_fh *old_dir, const char *old_name, - struct nfs_fh *new_dir, const char *new_name, - int must_be_dir) -{ - int *p, *p0; - int status; - int ruid = 0; - - /* - * Disallow "rename()" with trailing slashes over NFS: getting - * POSIX.1 behaviour is just too unlikely. - */ - if (must_be_dir) - return -EINVAL; - PRINTK("NFS call rename %s -> %s\n", old_name, new_name); - if (!(p0 = nfs_rpc_alloc(server->wsize))) - return -EIO; -retry: - p = nfs_rpc_header(p0, NFSPROC_RENAME, ruid); - p = xdr_encode_fhandle(p, old_dir); - p = xdr_encode_string(p, old_name); - p = xdr_encode_fhandle(p, new_dir); - p = xdr_encode_string(p, new_name); - if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) { - nfs_rpc_free(p0); - return status; - } - if (!(p = nfs_rpc_verify(p0))) - status = -errno_NFSERR_IO; - else if ((status = ntohl(*p++)) == NFS_OK) { - PRINTK("NFS reply rename\n"); - /* status = 0; */ - } - else { - if (!ruid && current->fsuid == 0 && current->uid != 0) { - ruid = 1; - goto retry; - } - PRINTK("NFS reply rename failed = %d\n", status); - status = -nfs_stat_to_errno(status); - } - nfs_rpc_free(p0); + dprintk("NFS call remove %s\n", name); + status = rpc_call(server->client, NFSPROC_REMOVE, &arg, NULL, 0); + dprintk("NFS reply remove: %d\n", status); return status; } -int nfs_proc_link(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fh *dir, const char *name) -{ - int *p, *p0; - int status; - int ruid = 0; - - PRINTK("NFS call link %s\n", name); - if (!(p0 = nfs_rpc_alloc(server->wsize))) - return -EIO; -retry: - p = nfs_rpc_header(p0, NFSPROC_LINK, ruid); - p = xdr_encode_fhandle(p, fhandle); - p = xdr_encode_fhandle(p, dir); - p = xdr_encode_string(p, name); - if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) { - nfs_rpc_free(p0); - return status; - } - if (!(p = nfs_rpc_verify(p0))) - status = -errno_NFSERR_IO; - else if ((status = ntohl(*p++)) == NFS_OK) { - PRINTK("NFS reply link\n"); - /* status = 0; */ - } - else { - if (!ruid && current->fsuid == 0 && current->uid != 0) { - ruid = 1; - goto retry; - } - PRINTK("NFS reply link failed = %d\n", status); - status = -nfs_stat_to_errno(status); - } - nfs_rpc_free(p0); +int +nfs_proc_rename(struct nfs_server *server, + struct nfs_fh *old_dir, const char *old_name, + struct nfs_fh *new_dir, const char *new_name) +{ + struct nfs_renameargs arg = { old_dir, old_name, new_dir, new_name }; + int status; + + dprintk("NFS call rename %s -> %s\n", old_name, new_name); + status = rpc_call(server->client, NFSPROC_RENAME, &arg, NULL, 0); + dprintk("NFS reply rename: %d\n", status); return status; } -int nfs_proc_symlink(struct nfs_server *server, struct nfs_fh *dir, - const char *name, const char *path, struct nfs_sattr *sattr) +int +nfs_proc_link(struct nfs_server *server, struct nfs_fh *fhandle, + struct nfs_fh *dir, const char *name) { - int *p, *p0; - int status; - int ruid = 0; - - PRINTK("NFS call symlink %s -> %s\n", name, path); - if (!(p0 = nfs_rpc_alloc(server->wsize))) - return -EIO; -retry: - p = nfs_rpc_header(p0, NFSPROC_SYMLINK, ruid); - p = xdr_encode_fhandle(p, dir); - p = xdr_encode_string(p, name); - p = xdr_encode_string(p, path); - p = xdr_encode_sattr(p, sattr); - if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) { - nfs_rpc_free(p0); - return status; - } - if (!(p = nfs_rpc_verify(p0))) - status = -errno_NFSERR_IO; - else if ((status = ntohl(*p++)) == NFS_OK) { - PRINTK("NFS reply symlink\n"); - /* status = 0; */ - } - else { - if (!ruid && current->fsuid == 0 && current->uid != 0) { - ruid = 1; - goto retry; - } - PRINTK("NFS reply symlink failed = %d\n", status); - status = -nfs_stat_to_errno(status); - } - nfs_rpc_free(p0); - return status; -} + struct nfs_linkargs arg = { fhandle, dir, name }; + int status; -int nfs_proc_mkdir(struct nfs_server *server, struct nfs_fh *dir, - const char *name, struct nfs_sattr *sattr, - struct nfs_fh *fhandle, struct nfs_fattr *fattr) -{ - int *p, *p0; - int status; - int ruid = 0; - - PRINTK("NFS call mkdir %s\n", name); - if (!(p0 = nfs_rpc_alloc(server->wsize))) - return -EIO; -retry: - p = nfs_rpc_header(p0, NFSPROC_MKDIR, ruid); - p = xdr_encode_fhandle(p, dir); - p = xdr_encode_string(p, name); - p = xdr_encode_sattr(p, sattr); - if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) { - nfs_rpc_free(p0); - return status; - } - if (!(p = nfs_rpc_verify(p0))) - status = -errno_NFSERR_IO; - else if ((status = ntohl(*p++)) == NFS_OK) { - p = xdr_decode_fhandle(p, fhandle); - p = xdr_decode_fattr(p, fattr); - PRINTK("NFS reply mkdir\n"); - /* status = 0; */ - } - else { - if (!ruid && current->fsuid == 0 && current->uid != 0) { - ruid = 1; - goto retry; - } - PRINTK("NFS reply mkdir failed = %d\n", status); - status = -nfs_stat_to_errno(status); - } - nfs_rpc_free(p0); + dprintk("NFS call link %s\n", name); + status = rpc_call(server->client, NFSPROC_LINK, &arg, NULL, 0); + dprintk("NFS reply link: %d\n", status); return status; } -int nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir, const char *name) -{ - int *p, *p0; - int status; - int ruid = 0; - - PRINTK("NFS call rmdir %s\n", name); - if (!(p0 = nfs_rpc_alloc(server->wsize))) - return -EIO; -retry: - p = nfs_rpc_header(p0, NFSPROC_RMDIR, ruid); - p = xdr_encode_fhandle(p, dir); - p = xdr_encode_string(p, name); - if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) { - nfs_rpc_free(p0); - return status; - } - if (!(p = nfs_rpc_verify(p0))) - status = -errno_NFSERR_IO; - else if ((status = ntohl(*p++)) == NFS_OK) { - PRINTK("NFS reply rmdir\n"); - /* status = 0; */ - } - else { - if (!ruid && current->fsuid == 0 && current->uid != 0) { - ruid = 1; - goto retry; - } - PRINTK("NFS reply rmdir failed = %d\n", status); - status = -nfs_stat_to_errno(status); - } - nfs_rpc_free(p0); +int +nfs_proc_symlink(struct nfs_server *server, struct nfs_fh *dir, + const char *name, const char *path, + struct nfs_sattr *sattr) +{ + struct nfs_symlinkargs arg = { dir, name, path, sattr }; + int status; + + dprintk("NFS call symlink %s -> %s\n", name, path); + status = rpc_call(server->client, NFSPROC_SYMLINK, &arg, NULL, 0); + dprintk("NFS reply symlink: %d\n", status); return status; } -int nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle, - int cookie, int count, struct nfs_entry *entry) -{ - int *p, *p0; - int status; - int ruid = 0; - int i; - int size; - int eof; - - PRINTK("NFS call readdir %d @ %d\n", count, cookie); - size = server->rsize; - if (!(p0 = nfs_rpc_alloc(server->rsize))) - return -EIO; -retry: - p = nfs_rpc_header(p0, NFSPROC_READDIR, ruid); - p = xdr_encode_fhandle(p, fhandle); - *p++ = htonl(cookie); - *p++ = htonl(size); - if ((status = nfs_rpc_call(server, p0, p, server->rsize)) < 0) { - nfs_rpc_free(p0); - return status; - } - if (!(p = nfs_rpc_verify(p0))) - status = -errno_NFSERR_IO; - else if ((status = ntohl(*p++)) == NFS_OK) { - for (i = 0; i < count && *p++; i++) { - if (!(p = xdr_decode_entry(p, entry++))) - break; - } - if (!p) { - printk("nfs_proc_readdir: giant filename\n"); - status = -errno_NFSERR_IO; - } - else { - eof = (i == count && !*p++ && *p++) - || (i < count && *p++); - if (eof && i) - entry[-1].eof = 1; - PRINTK("NFS reply readdir %d %s\n", i, - eof ? "eof" : ""); - status = i; - } - } - else { - if (!ruid && current->fsuid == 0 && current->uid != 0) { - ruid = 1; - goto retry; - } - PRINTK("NFS reply readdir failed = %d\n", status); - status = -nfs_stat_to_errno(status); - } - nfs_rpc_free(p0); +int +nfs_proc_mkdir(struct nfs_server *server, struct nfs_fh *dir, + const char *name, struct nfs_sattr *sattr, + struct nfs_fh *fhandle, struct nfs_fattr *fattr) +{ + struct nfs_createargs arg = { dir, name, sattr }; + struct nfs_diropok res = { fhandle, fattr }; + int status; + + dprintk("NFS call mkdir %s\n", name); + status = rpc_call(server->client, NFSPROC_MKDIR, &arg, &res, 0); + dprintk("NFS reply mkdir: %d\n", status); return status; } -int nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fsinfo *res) +int +nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir, const char *name) { - int *p, *p0; - int status; - int ruid = 0; - - PRINTK("NFS call statfs\n"); - if (!(p0 = nfs_rpc_alloc(server->rsize))) - return -EIO; -retry: - p = nfs_rpc_header(p0, NFSPROC_STATFS, ruid); - p = xdr_encode_fhandle(p, fhandle); - if ((status = nfs_rpc_call(server, p0, p, server->rsize)) < 0) { - nfs_rpc_free(p0); - return status; - } - if (!(p = nfs_rpc_verify(p0))) - status = -errno_NFSERR_IO; - else if ((status = ntohl(*p++)) == NFS_OK) { - p = xdr_decode_fsinfo(p, res); - PRINTK("NFS reply statfs\n"); - /* status = 0; */ - } - else { - if (!ruid && current->fsuid == 0 && current->uid != 0) { - ruid = 1; - goto retry; - } - PRINTK("NFS reply statfs failed = %d\n", status); - status = -nfs_stat_to_errno(status); - } - nfs_rpc_free(p0); + struct nfs_diropargs arg = { dir, name }; + int status; + + dprintk("NFS call rmdir %s\n", name); + status = rpc_call(server->client, NFSPROC_RMDIR, &arg, NULL, 0); + dprintk("NFS reply rmdir: %d\n", status); return status; } /* - * Here are a few RPC-assist functions. + * The READDIR implementation is somewhat hackish - we pass a temporary + * buffer to the encode function, which installs it in the receive + * iovec. The dirent buffer itself is passed in the result struct. */ - -int *rpc_header(int *p, int procedure, int program, int version, - int uid, int gid, - int ngroup, gid_t *groups) -{ - int *p1; - static int xid = 0; - unsigned char *sys = (unsigned char *) system_utsname.nodename; - - if (xid == 0) { - xid = CURRENT_TIME; - xid ^= (sys[3]<<24) | (sys[2]<<16) | (sys[1]<<8) | sys[0]; - } - *p++ = htonl(++xid); - *p++ = htonl(RPC_CALL); - *p++ = htonl(RPC_VERSION); - *p++ = htonl(program); - *p++ = htonl(version); - *p++ = htonl(procedure); - *p++ = htonl(RPC_AUTH_UNIX); - p1 = p++; - *p++ = htonl(CURRENT_TIME); /* traditional, could be anything */ - p = xdr_encode_string(p, (char *) sys); - *p++ = htonl(uid); - *p++ = htonl(gid); - if (ngroup > 16) - ngroup = 16; - *p++ = htonl(ngroup); - while (ngroup) { - *p++ = htonl(*groups); - groups++; - ngroup--; - } - *p1 = htonl((p - (p1 + 1)) << 2); - *p++ = htonl(RPC_AUTH_NULL); - *p++ = htonl(0); - return p; -} - - -static int *nfs_rpc_header(int *p, int procedure, int ruid) -{ - return rpc_header(p, procedure, NFS_PROGRAM, NFS_VERSION, - (ruid ? current->uid : current->fsuid), - current->egid, current->ngroups, current->groups); -} - - -int *rpc_verify(int *p) +int +nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle, + u32 cookie, unsigned int size, struct nfs_entry *entry) { - unsigned int n; - - p++; - if ((n = ntohl(*p++)) != RPC_REPLY) { - printk("nfs_rpc_verify: not an RPC reply: %x\n", n); - return NULL; - } - if ((n = ntohl(*p++)) != RPC_MSG_ACCEPTED) { - printk("nfs_rpc_verify: RPC call rejected: %d\n", n); - return NULL; - } - switch (n = ntohl(*p++)) { - case RPC_AUTH_NULL: case RPC_AUTH_UNIX: case RPC_AUTH_SHORT: - break; - default: - printk("nfs_rpc_verify: bad RPC authentication type: %d\n", n); - return NULL; - } - if ((n = ntohl(*p++)) > 400) { - printk("nfs_rpc_verify: giant auth size\n"); - return NULL; - } - p += QUADLEN(n); - if ((n = ntohl(*p++)) != RPC_SUCCESS) { - printk("nfs_rpc_verify: RPC call failed: %d\n", n); - return NULL; + struct nfs_readdirargs arg; + struct nfs_readdirres res; + void * buffer; + int status; + + /* First get a temp buffer for the readdir reply */ + while (!(buffer = (void *) get_free_page(GFP_USER))) { + need_resched = 1; + schedule(); + if (signalled()) + return -ERESTARTSYS; } - return p; -} - -static int *nfs_rpc_verify(int *p) -{ - return rpc_verify(p); + arg.fh = fhandle; + arg.cookie = cookie; + arg.buffer = buffer; + arg.bufsiz = server->rsize < PAGE_SIZE? server->rsize : PAGE_SIZE; + res.buffer = entry; + res.bufsiz = size; + + dprintk("NFS call readdir %d\n", cookie); + status = rpc_call(server->client, NFSPROC_READDIR, &arg, &res, 0); + dprintk("NFS reply readdir: %d\n", status); + free_page((unsigned long) buffer); + return status; } - -/* - * We need to translate between nfs status return values and - * the local errno values which may not be the same. - */ - -static struct { - int stat; - int errno; -} nfs_errtbl[] = { - { NFS_OK, 0 }, - { NFSERR_PERM, EPERM }, - { NFSERR_NOENT, ENOENT }, - { NFSERR_IO, errno_NFSERR_IO }, - { NFSERR_NXIO, ENXIO }, - { NFSERR_EAGAIN, EAGAIN }, - { NFSERR_ACCES, EACCES }, - { NFSERR_EXIST, EEXIST }, - { NFSERR_NODEV, ENODEV }, - { NFSERR_NOTDIR, ENOTDIR }, - { NFSERR_ISDIR, EISDIR }, - { NFSERR_INVAL, EINVAL }, - { NFSERR_FBIG, EFBIG }, - { NFSERR_NOSPC, ENOSPC }, - { NFSERR_ROFS, EROFS }, - { NFSERR_NAMETOOLONG, ENAMETOOLONG }, - { NFSERR_NOTEMPTY, ENOTEMPTY }, - { NFSERR_DQUOT, EDQUOT }, - { NFSERR_STALE, ESTALE }, -#ifdef EWFLUSH - { NFSERR_WFLUSH, EWFLUSH }, -#endif - { -1, EIO } -}; - -static int nfs_stat_to_errno(int stat) +int +nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, + struct nfs_fsinfo *info) { - int i; + int status; - for (i = 0; nfs_errtbl[i].stat != -1; i++) { - if (nfs_errtbl[i].stat == stat) - return nfs_errtbl[i].errno; - } - printk("nfs_stat_to_errno: bad nfs status return value: %d\n", stat); - return nfs_errtbl[i].errno; + dprintk("NFS call statfs\n"); + status = rpc_call(server->client, NFSPROC_STATFS, fhandle, info, 0); + dprintk("NFS reply statfs: %d\n", status); + return status; } - diff -u --recursive --new-file v2.1.31/linux/fs/nfs/read.c linux/fs/nfs/read.c --- v2.1.31/linux/fs/nfs/read.c Wed Dec 31 16:00:00 1969 +++ linux/fs/nfs/read.c Fri Apr 4 11:05:59 1997 @@ -0,0 +1,232 @@ +/* + * linux/fs/nfs/read.c + * + * Block I/O for NFS + * + * Partial copy of Linus' read cache modifications to fs/nfs/file.c + * modified for async RPC by okir@monad.swb.de + * + * We do an ugly hack here in order to return proper error codes to the + * user program when a read request failed: since generic_file_read + * only checks the return value of inode->i_op->readpage() which is always 0 + * for async RPC, we set the error bit of the page to 1 when an error occurs, + * and make nfs_readpage transmit requests synchronously when encountering this. + * This is only a small problem, though, since we now retry all operations + * within the RPC code when root squashing is suspected. + */ + +#define NFS_NEED_XDR_TYPES +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define NFSDBG_FACILITY NFSDBG_PAGECACHE + +struct nfs_rreq { + struct inode * ra_inode; /* inode from which to read */ + struct page * ra_page; /* page to be read */ + struct nfs_readargs ra_args; /* XDR argument struct */ + struct nfs_readres ra_res; /* ... and result struct */ + struct nfs_fattr ra_fattr; /* fattr storage */ +}; + +/* Hack for future NFS swap support */ +#ifndef IS_SWAPFILE +# define IS_SWAPFILE(inode) (0) +#endif + + +/* + * Set up the NFS read request struct + */ +static inline void +nfs_readreq_setup(struct nfs_rreq *req, struct nfs_fh *fh, + unsigned long offset, void *buffer, unsigned int rsize) +{ + req->ra_args.fh = fh; + req->ra_args.offset = offset; + req->ra_args.count = rsize; + req->ra_args.buffer = buffer; + req->ra_res.fattr = &req->ra_fattr; + req->ra_res.count = rsize; +} + + +/* + * Read a page synchronously. + */ +int +nfs_readpage_sync(struct inode *inode, struct page *page) +{ + struct nfs_rreq rqst; + unsigned long offset = page->offset; + char *buffer = (char *) page_address(page); + int rsize = NFS_SERVER(inode)->rsize; + int result, refresh = 0; + int count = PAGE_SIZE; + int flags = IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0; + + dprintk("NFS: nfs_readpage_sync(%p)\n", page); + clear_bit(PG_error, &page->flags); + + do { + if (count < rsize) + rsize = count; + + dprintk("NFS: nfs_proc_read(%s, (%x,%lx), %ld, %d, %p)\n", + NFS_SERVER(inode)->hostname, inode->i_dev, + inode->i_ino, offset, rsize, buffer); + + /* Set up arguments and perform rpc call */ + nfs_readreq_setup(&rqst, NFS_FH(inode), offset, buffer, rsize); + result = rpc_call(NFS_CLIENT(inode), NFSPROC_READ, + &rqst.ra_args, &rqst.ra_res, flags); + + /* + * Even if we had a partial success we can't mark the page + * cache valid. + */ + if (result < 0) { + if (result == -EISDIR) + result = -EINVAL; + goto io_error; + } + refresh = 1; + count -= result; + offset += result; + buffer += result; + if (result < rsize) /* NFSv2ism */ + break; + } while (count); + + memset(buffer, 0, count); + set_bit(PG_uptodate, &page->flags); + result = 0; + +io_error: + if (refresh) + nfs_refresh_inode(inode, &rqst.ra_fattr); + clear_bit(PG_locked, &page->flags); + wake_up(&page->wait); + return result; +} + +/* + * This is the callback from RPC telling us whether a reply was + * received or some error occurred (timeout or socket shutdown). + */ +static void +nfs_readpage_result(struct rpc_task *task) +{ + struct nfs_rreq *req = (struct nfs_rreq *) task->tk_calldata; + struct page *page = req->ra_page; + int result = task->tk_status; + static int succ = 0, fail = 0; + + dprintk("NFS: %4d received callback for page %lx, result %d\n", + task->tk_pid, page_address(page), result); + + if (result >= 0) { + result = req->ra_res.count; + if (result < PAGE_SIZE) { + memset((char *) page_address(page) + result, 0, + PAGE_SIZE - result); + } + nfs_refresh_inode(req->ra_inode, &req->ra_fattr); + set_bit(PG_uptodate, &page->flags); + succ++; + } else { + set_bit(PG_error, &page->flags); + fail++; + dprintk("NFS: %d successful reads, %d failures\n", succ, fail); + } + iput(req->ra_inode); + clear_bit(PG_locked, &page->flags); + wake_up(&page->wait); + + free_page(page_address(page)); + + rpc_release_task(task); + kfree(req); +} + +static inline int +nfs_readpage_async(struct inode *inode, struct page *page) +{ + struct nfs_rreq *req; + int result, flags; + + dprintk("NFS: nfs_readpage_async(%p)\n", page); + flags = RPC_TASK_ASYNC | (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0); + + if (NFS_CONGESTED(inode) + || !(req = (struct nfs_rreq *) rpc_allocate(flags, sizeof(*req)))) { + dprintk("NFS: deferring async READ request.\n"); + return -1; + } + + /* Initialize request */ + nfs_readreq_setup(req, NFS_FH(inode), page->offset, + (void *) page_address(page), PAGE_SIZE); + req->ra_inode = inode; + req->ra_page = page; + + /* Start the async call */ + dprintk("NFS: executing async READ request.\n"); + result = rpc_do_call(NFS_CLIENT(inode), NFSPROC_READ, + &req->ra_args, &req->ra_res, flags, + nfs_readpage_result, req); + + if (result >= 0) { + inode->i_count++; + page->count++; + return 0; + } + + dprintk("NFS: failed to enqueue async READ request.\n"); + kfree(req); + return -1; +} + +/* + * Read a page over NFS. + * We read the page synchronously in the following cases: + * - The file is a swap file. Swap-ins are always sync operations, + * so there's no need bothering to make async reads 100% fail-safe. + * - The NFS rsize is smaller than PAGE_SIZE. We could kludge our way + * around this by creating several consecutive read requests, but + * that's hardly worth it. + * - The error flag is set for this page. This happens only when a + * previous async read operation failed. + * - The server is congested. + */ +int +nfs_readpage(struct inode *inode, struct page *page) +{ + unsigned long address; + int error = -1; + + dprintk("NFS: nfs_readpage %08lx\n", page_address(page)); + set_bit(PG_locked, &page->flags); + address = page_address(page); + page->count++; + if (!IS_SWAPFILE(inode) && !PageError(page) + && NFS_SERVER(inode)->rsize >= PAGE_SIZE) + error = nfs_readpage_async(inode, page); + if (error < 0) /* couldn't enqueue */ + error = nfs_readpage_sync(inode, page); + if (error < 0 && IS_SWAPFILE(inode)) + printk("Aiee.. nfs swap-in of page failed!\n"); + free_page(address); + return error; +} diff -u --recursive --new-file v2.1.31/linux/fs/nfs/rpcsock.c linux/fs/nfs/rpcsock.c --- v2.1.31/linux/fs/nfs/rpcsock.c Sat Jan 25 13:46:13 1997 +++ linux/fs/nfs/rpcsock.c Wed Dec 31 16:00:00 1969 @@ -1,588 +0,0 @@ -/* - * linux/fs/nfs/rpcsock.c - * - * This is a generic RPC call interface for datagram sockets that is able - * to place several concurrent RPC requests at the same time. It works like - * this: - * - * - When a process places a call, it allocates a request slot if - * one is available. Otherwise, it sleeps on the backlog queue - * (rpc_reserve). - * - Then, the message is transmitted via rpc_send (exported by name of - * rpc_transmit). - * - Finally, the process waits for the call to complete (rpc_doio): - * The first process on the receive queue waits for the next RPC packet, - * and peeks at the XID. If it finds a matching request, it receives - * the datagram on behalf of that process and wakes it up. Otherwise, - * the datagram is discarded. - * - If the process having received the datagram was the first one on - * the receive queue, it wakes up the next one to listen for replies. - * - It then removes itself from the request queue (rpc_release). - * If there are more callers waiting on the backlog queue, they are - * woken up, too. - * - * Mar 1996: - * - Split up large functions into smaller chunks as per Linus' coding - * style. Found an interesting bug this way, too. - * - Added entry points for nfsiod. - * - * Copyright (C) 1995, 1996, Olaf Kirch - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#define msleep(sec) { current->timeout = sec * HZ / 1000; \ - current->state = TASK_INTERRUPTIBLE; \ - schedule(); \ - } - -#undef DEBUG_RPC -#ifdef DEBUG_RPC -#define dprintk(args...) printk(## args) -#else -#define dprintk(args...) -#endif - - -/* - * Insert new request into wait list. We make sure list is sorted by - * increasing timeout value. - */ -static inline void -rpc_insque(struct rpc_sock *rsock, struct rpc_wait *slot) -{ - struct rpc_wait *next = rsock->pending; - - slot->w_next = next; - slot->w_prev = NULL; - if (next) - next->w_prev = slot; - rsock->pending = slot; - slot->w_queued = 1; - - dprintk("RPC: inserted %p into queue\n", slot); -} - -/* - * Remove request from request queue - */ -static inline void -rpc_remque(struct rpc_sock *rsock, struct rpc_wait *slot) -{ - struct rpc_wait *prev = slot->w_prev, - *next = slot->w_next; - - if (prev != NULL) - prev->w_next = next; - else - rsock->pending = next; - if (next != NULL) - next->w_prev = prev; - - slot->w_queued = 0; - dprintk("RPC: removed %p from queue, head now %p.\n", - slot, rsock->pending); -} - -/* - * Write data to socket. - */ -static inline int -rpc_sendmsg(struct rpc_sock *rsock, struct iovec *iov, int nr, int len, - struct sockaddr *sap, int salen) -{ - struct socket *sock = rsock->sock; - struct msghdr msg; - unsigned long oldfs; - int result; - - msg.msg_iov = iov; - msg.msg_iovlen = nr; - msg.msg_name = sap; - msg.msg_namelen = salen; - msg.msg_control = NULL; - msg.msg_controllen = 0; - msg.msg_flags = 0; - - oldfs = get_fs(); - set_fs(get_ds()); - result = sock_sendmsg(sock, &msg, len); - set_fs(oldfs); - - dprintk("RPC: rpc_sendmsg(iov %p, len %d) = %d\n", iov, len, result); - return result; -} -/* - * Read data from socket - */ -static inline int -rpc_recvmsg(struct rpc_sock *rsock, struct iovec *iov, - int nr, int len, int flags) -{ - struct socket *sock = rsock->sock; - struct sockaddr sa; - struct msghdr msg; - unsigned long oldfs; - int result; - - msg.msg_iov = iov; - msg.msg_iovlen = nr; - msg.msg_name = &sa; - msg.msg_namelen = sizeof(sa); - msg.msg_control = NULL; - msg.msg_controllen = 0; - msg.msg_flags = 0; - - oldfs = get_fs(); - set_fs(get_ds()); - result = sock_recvmsg(sock, &msg, len, flags|MSG_DONTWAIT); - set_fs(oldfs); - - dprintk("RPC: rpc_recvmsg(iov %p, len %d) = %d\n", iov, len, result); - return result; -} - -/* - * This code is slightly complicated. Since the networking code does not - * honor the current->timeout value, we have to poll on the socket. - */ -static inline int -rpc_select(struct rpc_sock *rsock) -{ - struct poll_table_entry entry; - struct file *file = rsock->file; - poll_table wait_table; - - dprintk("RPC: polling socket...\n"); - wait_table.nr = 0; - wait_table.entry = &entry; - current->state = TASK_INTERRUPTIBLE; - if (!(file->f_op->poll(file, &wait_table) & POLLIN)) { - schedule(); - remove_wait_queue(entry.wait_address, &entry.wait); - current->state = TASK_RUNNING; - if (current->signal & ~current->blocked) - return -ERESTARTSYS; - if (current->timeout == 0) - return -ETIMEDOUT; - } else if (wait_table.nr) - remove_wait_queue(entry.wait_address, &entry.wait); - current->state = TASK_RUNNING; - dprintk("RPC: ...Okay, there appears to be some data.\n"); - return 0; -} - -/* - * Reserve an RPC call slot. nocwait determines whether we wait in case - * of congestion or not. - */ -int -rpc_reserve(struct rpc_sock *rsock, struct rpc_ioreq *req, int nocwait) -{ - struct rpc_wait *slot; - - req->rq_slot = NULL; - - while (!(slot = rsock->free) || rsock->cong >= rsock->cwnd) { - if (nocwait) { - current->timeout = 0; - return -ENOBUFS; - } - dprintk("RPC: rpc_reserve waiting on backlog\n"); - interruptible_sleep_on(&rsock->backlog); - if (current->timeout == 0) - return -ETIMEDOUT; - if (current->signal & ~current->blocked) - return -ERESTARTSYS; - if (rsock->shutdown) - return -EIO; - } - - rsock->free = slot->w_next; - rsock->cong += RPC_CWNDSCALE; /* bump congestion value */ - - slot->w_queued = 0; - slot->w_gotit = 0; - slot->w_req = req; - - dprintk("RPC: reserved slot %p\n", slot); - req->rq_slot = slot; - return 0; -} - -/* - * Release an RPC call slot - */ -void -rpc_release(struct rpc_sock *rsock, struct rpc_ioreq *req) -{ - struct rpc_wait *slot = req->rq_slot; - - if (slot != NULL) { - dprintk("RPC: release slot %p\n", slot); - - /* Wake up the next receiver */ - if (slot == rsock->pending && slot->w_next != NULL) - wake_up(&slot->w_next->w_wait); - - /* remove slot from queue of pending */ - if (slot->w_queued) - rpc_remque(rsock, slot); - slot->w_next = rsock->free; - rsock->free = slot; - - /* decrease congestion value */ - rsock->cong -= RPC_CWNDSCALE; - if (rsock->cong < rsock->cwnd && rsock->backlog) - wake_up(&rsock->backlog); - if (rsock->shutdown) - wake_up(&rsock->shutwait); - - req->rq_slot = NULL; - } -} - -/* - * Adjust RPC congestion window - */ -static void -rpc_cwnd_adjust(struct rpc_sock *rsock, int timeout) -{ - unsigned long cwnd = rsock->cwnd; - - if (!timeout) { - if (rsock->cong >= cwnd) { - /* The (cwnd >> 1) term makes sure - * the result gets rounded properly. */ - cwnd += (RPC_CWNDSCALE * RPC_CWNDSCALE + - (cwnd >> 1)) / cwnd; - if (cwnd > RPC_MAXCWND) - cwnd = RPC_MAXCWND; - } - } else { - if ((cwnd >>= 1) < RPC_CWNDSCALE) - cwnd = RPC_CWNDSCALE; - dprintk("RPC: cwnd decrease %08lx\n", cwnd); - } - dprintk("RPC: cong %08lx, cwnd was %08lx, now %08lx\n", - rsock->cong, rsock->cwnd, cwnd); - - rsock->cwnd = cwnd; -} - -static inline void -rpc_send_check(char *where, u32 *ptr) -{ - if (ptr[1] != htonl(RPC_CALL) || ptr[2] != htonl(RPC_VERSION)) { - printk("RPC: %s sending evil packet:\n" - " %08x %08x %08x %08x %08x %08x %08x %08x\n", - where, - ptr[0], ptr[1], ptr[2], ptr[3], - ptr[4], ptr[5], ptr[6], ptr[7]); - } -} - -/* - * Place the actual RPC call. - * We have to copy the iovec because sendmsg fiddles with its contents. - */ -static inline int -rpc_send(struct rpc_sock *rsock, struct rpc_wait *slot) -{ - struct rpc_ioreq *req = slot->w_req; - struct iovec iov[UIO_FASTIOV]; - - if (rsock->shutdown) - return -EIO; - - memcpy(iov, req->rq_svec, req->rq_snr * sizeof(iov[0])); - slot->w_xid = *(u32 *)(iov[0].iov_base); - if (!slot->w_queued) - rpc_insque(rsock, slot); - - dprintk("rpc_send(%p, %x)\n", slot, slot->w_xid); - rpc_send_check("rpc_send", (u32 *) req->rq_svec[0].iov_base); - return rpc_sendmsg(rsock, iov, req->rq_snr, req->rq_slen, - req->rq_addr, req->rq_alen); -} - -/* - * This is the same as rpc_send but for the functions exported to nfsiod - */ -int -rpc_transmit(struct rpc_sock *rsock, struct rpc_ioreq *req) -{ - rpc_send_check("rpc_transmit", (u32 *) req->rq_svec[0].iov_base); - return rpc_send(rsock, req->rq_slot); -} - -/* - * Receive and dispatch a single reply - */ -static inline int -rpc_grok(struct rpc_sock *rsock) -{ - struct rpc_wait *rovr; - struct rpc_ioreq *req; - struct iovec iov[UIO_FASTIOV]; - u32 xid; - int safe, result; - - iov[0].iov_base = (void *) &xid; - iov[0].iov_len = sizeof(xid); - result = rpc_recvmsg(rsock, iov, 1, sizeof(xid), MSG_PEEK); - - if (result < 0) { - switch (-result) { - case EAGAIN: case ECONNREFUSED: - return 0; - case ERESTARTSYS: - return result; - default: - dprintk("rpc_grok: recv error = %d\n", result); - } - } - if (result < 4) { - printk(KERN_WARNING "RPC: impossible RPC reply size %d\n", - result); - iov[0].iov_base=(void*)&xid; /* xid=32bits, which is large enough */ - iov[0].iov_len=result; - rpc_recvmsg(rsock, iov, 1, result, 0); - return 0; - } - - dprintk("RPC: rpc_grok: got xid %08lx\n", (unsigned long) xid); - - /* Look for the caller */ - safe = 0; - for (rovr = rsock->pending; rovr; rovr = rovr->w_next) { - if (rovr->w_xid == xid) - break; - if (safe++ > RPC_MAXREQS) { - printk(KERN_WARNING "RPC: loop in request Q!!\n"); - rovr = NULL; - break; - } - } - - if (!rovr || rovr->w_gotit) { - /* discard dgram */ - dprintk("RPC: rpc_grok: %s.\n", - rovr? "duplicate reply" : "bad XID"); - iov[0].iov_base = (void *) &xid; - iov[0].iov_len = sizeof(xid); - rpc_recvmsg(rsock, iov, 1, sizeof(xid), 0); - return 0; - } - req = rovr->w_req; - - /* Now receive the reply... Copy the iovec first because of - * memcpy_fromiovec fiddling. */ - memcpy(iov, req->rq_rvec, req->rq_rnr * sizeof(iov[0])); - result = rpc_recvmsg(rsock, iov, req->rq_rnr, req->rq_rlen, 0); - rovr->w_result = result; - rovr->w_gotit = 1; - - /* ... and wake up the process */ - wake_up(&rovr->w_wait); - - return result; -} - -/* - * Wait for the reply to our call. - */ -static int -rpc_recv(struct rpc_sock *rsock, struct rpc_wait *slot) -{ - int result; - - do { - /* If we are not the receiver, wait on the sidelines */ - dprintk("RPC: rpc_recv TP1\n"); - while (rsock->pending != slot) { - if (!slot->w_gotit) - interruptible_sleep_on(&slot->w_wait); - if (slot->w_gotit) - return slot->w_result; /* quite important */ - if (current->signal & ~current->blocked) - return -ERESTARTSYS; - if (rsock->shutdown) - return -EIO; - if (current->timeout == 0) - return -ETIMEDOUT; - } - - /* Wait for data to arrive */ - if ((result = rpc_select(rsock)) < 0) { - dprintk("RPC: select error = %d\n", result); - return result; - } - - /* Receive and dispatch */ - if ((result = rpc_grok(rsock)) < 0) - return result; - } while (current->timeout && !slot->w_gotit); - - return slot->w_gotit? slot->w_result : -ETIMEDOUT; -} - -/* - * Generic RPC call routine. This handles retries and timeouts etc pp. - * - * If sent is non-null, it assumes the called has already sent out the - * message, so it won't need to do so unless a timeout occurs. - */ -int -rpc_doio(struct rpc_sock *rsock, struct rpc_ioreq *req, - struct rpc_timeout *strategy, int sent) -{ - struct rpc_wait *slot; - int result, retries; - unsigned long timeout; - - timeout = strategy->to_initval; - retries = 0; - slot = req->rq_slot; - - do { - dprintk("RPC: rpc_doio: TP1 (req %p)\n", req); - current->timeout = jiffies + timeout; - if (slot == NULL) { - result = rpc_reserve(rsock, req, 0); - if (result == -ETIMEDOUT) - goto timedout; - if (result < 0) - break; - slot = req->rq_slot; - rpc_send_check("rpc_doio", - (u32 *) req->rq_svec[0].iov_base); - rpc_insque(rsock, slot); - } - - /* This check is for loopback NFS. Sometimes replies come - * in before biod has called rpc_doio... */ - if (slot->w_gotit) { - result = slot->w_result; - break; - } - - dprintk("RPC: rpc_doio: TP2\n"); - if (sent || (result = rpc_send(rsock, slot)) >= 0) { - result = rpc_recv(rsock, slot); - sent = 0; - } - - if (result != -ETIMEDOUT) { - /* dprintk("RPC: rpc_recv returned %d\n", result); */ - rpc_cwnd_adjust(rsock, 0); - break; - } - - rpc_cwnd_adjust(rsock, 1); - -timedout: - dprintk("RPC: rpc_recv returned timeout.\n"); - if (strategy->to_exponential) - timeout <<= 1; - else - timeout += strategy->to_increment; - if (strategy->to_maxval && timeout >= strategy->to_maxval) - timeout = strategy->to_maxval; - if (strategy->to_retries && ++retries >= strategy->to_retries) - break; - } while (1); - - dprintk("RPC: rpc_doio: TP3\n"); - current->timeout = 0; - return result; -} - -/* - */ -int -rpc_call(struct rpc_sock *rsock, struct rpc_ioreq *req, - struct rpc_timeout *strategy) -{ - int result; - - result = rpc_doio(rsock, req, strategy, 0); - if (req->rq_slot == NULL) - printk(KERN_WARNING "RPC: bad: rq_slot == NULL\n"); - rpc_release(rsock, req); - return result; -} - -struct rpc_sock * -rpc_makesock(struct file *file) -{ - struct rpc_sock *rsock; - struct socket *sock; - struct sock *sk; - struct rpc_wait *slot; - int i; - - dprintk("RPC: make RPC socket...\n"); - sock = &file->f_inode->u.socket_i; - if (sock->type != SOCK_DGRAM || sock->ops->family != AF_INET) { - printk(KERN_WARNING "RPC: only UDP sockets supported\n"); - return NULL; - } - sk = sock->sk; - - if ((rsock = kmalloc(sizeof(struct rpc_sock), GFP_KERNEL)) == NULL) - return NULL; - memset(rsock, 0, sizeof(*rsock)); /* Nnnngh! */ - - rsock->sock = sock; - rsock->inet = sk; - rsock->file = file; - rsock->cwnd = RPC_INITCWND; - - dprintk("RPC: slots %p, %p, ...\n", rsock->waiting, rsock->waiting + 1); - rsock->free = rsock->waiting; - for (i = 0, slot = rsock->waiting; i < RPC_MAXREQS-1; i++, slot++) - slot->w_next = slot + 1; - slot->w_next = NULL; - - dprintk("RPC: made socket %p\n", rsock); - return rsock; -} - -int -rpc_closesock(struct rpc_sock *rsock) -{ - unsigned long t0 = jiffies; - - rsock->shutdown = 1; - while (rsock->pending || waitqueue_active(&rsock->backlog)) { - interruptible_sleep_on(&rsock->shutwait); - if (current->signal & ~current->blocked) - return -EINTR; -#if 1 - if (t0 && t0 - jiffies > 60 * HZ) { - printk(KERN_WARNING "RPC: hanging in rpc_closesock.\n"); - t0 = 0; - } -#endif - } - - kfree(rsock); - return 0; -} diff -u --recursive --new-file v2.1.31/linux/fs/nfs/sock.c linux/fs/nfs/sock.c --- v2.1.31/linux/fs/nfs/sock.c Mon Oct 28 04:29:26 1996 +++ linux/fs/nfs/sock.c Wed Dec 31 16:00:00 1969 @@ -1,127 +0,0 @@ -/* - * linux/fs/nfs/sock.c - * - * Copyright (C) 1992, 1993 Rick Sladkey - * - * low-level nfs remote procedure call interface - * - * FIXES - * - * 2/7/94 James Bottomley and Jon Peatfield DAMTP, Cambridge University - * - * An xid mismatch no longer causes the request to be trashed. - * - * Peter Eriksson - incorrect XID used to confuse Linux - * Florian La Roche - use the correct max size, if reading a packet and - * also verify, if the whole packet has been read... - * more checks should be done in proc.c... - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define _S(nr) (1<<((nr)-1)) - -/* - * Place a synchronous call to the NFS server, meaning that the process - * sleeps in rpc_call until it either receives a reply or a major timeout - * occurs. - * This is now merely a front-end to nfs_rpc_doio. - */ -int -nfs_rpc_call(struct nfs_server *server, int *start, int *end, int size) -{ - struct rpc_ioreq req; - - size += 1024; /* account for NFS slack space. ugly */ - - req.rq_addr = &server->toaddr; - req.rq_alen = sizeof(server->toaddr); - req.rq_slot = NULL; - - req.rq_svec[0].iov_base = start; - req.rq_svec[0].iov_len = (end - start) << 2; - req.rq_slen = (end - start) << 2; - req.rq_snr = 1; - req.rq_rvec[0].iov_base = start; - req.rq_rvec[0].iov_len = size; - req.rq_rlen = size; - req.rq_rnr = 1; - - return nfs_rpc_doio(server, &req, 0); -} - -int -nfs_rpc_doio(struct nfs_server *server, struct rpc_ioreq *req, int async) -{ - struct rpc_timeout timeout; - unsigned long maxtimeo; - unsigned long oldmask; - int major_timeout_seen, result; - - timeout.to_initval = server->timeo; - timeout.to_maxval = NFS_MAX_RPC_TIMEOUT*HZ/10; - timeout.to_retries = server->retrans; - timeout.to_exponential = 1; - - oldmask = current->blocked; - current->blocked |= ~(_S(SIGKILL) - | ((server->flags & NFS_MOUNT_INTR) - ? ((current->sig->action[SIGINT - 1].sa_handler == SIG_DFL - ? _S(SIGINT) : 0) - | (current->sig->action[SIGQUIT - 1].sa_handler == SIG_DFL - ? _S(SIGQUIT) : 0)) - : 0)); - - major_timeout_seen = 0; - maxtimeo = timeout.to_maxval; - - do { - result = rpc_doio(server->rsock, req, &timeout, async); - rpc_release(server->rsock, req); /* Release slot */ - - if (current->signal & ~current->blocked) - result = -ERESTARTSYS; - if (result == -ETIMEDOUT) { - if (async) - break; - if (server->flags & NFS_MOUNT_SOFT) { - printk("NFS server %s not responding, " - "timed out.\n", server->hostname); - result = -EIO; - break; - } - if (!major_timeout_seen) { - printk("NFS server %s not responding, " - "still trying.\n", server->hostname); - major_timeout_seen = 1; - } - if ((timeout.to_initval <<= 1) >= maxtimeo) { - timeout.to_initval = maxtimeo; - } - } else if (result < 0 && result != -ERESTARTSYS) { - printk("NFS: notice message: result = %d.\n", result); - } - } while (result == -ETIMEDOUT && !(server->flags & NFS_MOUNT_SOFT)); - - if (result >= 0 && major_timeout_seen) - printk("NFS server %s OK.\n", server->hostname); - /* 20 is the minimum RPC reply header size */ - if (result >= 0 && result < 20) { - printk("NFS: too small read memory size (%d bytes)\n", result); - result = -EIO; - } - - current->blocked = oldmask; - return result; -} diff -u --recursive --new-file v2.1.31/linux/fs/nfs/symlink.c linux/fs/nfs/symlink.c --- v2.1.31/linux/fs/nfs/symlink.c Tue Dec 31 11:19:32 1996 +++ linux/fs/nfs/symlink.c Sat Apr 5 12:39:31 1997 @@ -48,9 +48,10 @@ static int nfs_follow_link(struct inode *dir, struct inode *inode, int flag, int mode, struct inode **res_inode) { - int error, *mem; + int error; unsigned int len; char *res, *res2; + void *mem; *res_inode = NULL; if (!dir) { @@ -95,9 +96,12 @@ static int nfs_readlink(struct inode *inode, char *buffer, int buflen) { - int error, *mem; + int error; unsigned int len; char *res; + void *mem; + + dfprintk(VFS, "nfs: readlink(%x/%ld)\n", inode->i_dev, inode->i_ino); if (!S_ISLNK(inode->i_mode)) { iput(inode); @@ -108,12 +112,10 @@ error = nfs_proc_readlink(NFS_SERVER(inode), NFS_FH(inode), &mem, &res, &len, buflen); iput(inode); - if (!error) { - error = copy_to_user(buffer, res, len); - if (!error) - error = put_user('\0', buffer + len); - if (!error) - error = len; + if (! error) { + copy_to_user(buffer, res, len); + put_user('\0', buffer + len); + error = len; } kfree(mem); return error; diff -u --recursive --new-file v2.1.31/linux/fs/nfs/write.c linux/fs/nfs/write.c --- v2.1.31/linux/fs/nfs/write.c Wed Dec 31 16:00:00 1969 +++ linux/fs/nfs/write.c Sat Apr 5 12:39:52 1997 @@ -0,0 +1,823 @@ +/* + * linux/fs/nfs/write.c + * + * Writing file data over NFS. + * + * We do it like this: When a (user) process wishes to write data to an + * NFS file, a write request is allocated that contains the RPC task data + * plus some info on the page to be written, and added to the inode's + * write chain. If the process writes past the end of the page, an async + * RPC call to write the page is scheduled immediately; otherwise, the call + * is delayed for a few seconds. + * + * Just like readahead, no async I/O is performed if wsize < PAGE_SIZE. + * + * Write requests are kept on the inode's writeback list. Each entry in + * that list references the page (portion) to be written. When the + * cache timeout has expired, the RPC task is woken up, and tries to + * lock the page. As soon as it manages to do so, the request is moved + * from the writeback list to the writelock list. + * + * Note: we must make sure never to confuse the inode passed in the + * write_page request with the one in page->inode. As far as I understant + * it, these are different when doing a swap-out. + * + * To understand everything that goes one here and in the nfs read code, + * one should be aware that a page is locked in exactly one of the following + * cases: + * + * - A write request is in progress. + * - A user process is in generic_file_write/nfs_update_page + * - A user process is in generic_file_read + * + * Also note that because of the way pages are invalidated in + * nfs_revalidate_inode, the following assertions hold: + * + * - If a page is dirty, there will be no read requests (a page will + * not be re-read unless invalidated by nfs_revalidate_inode). + * - If the page is not uptodate, there will be no pending write + * requests, and no process will be in nfs_update_page. + * + * FIXME: Interaction with the vmscan routines is not optimal yet. + * Either vmscan must be made nfs-savvy, or we need a different page + * reclaim concept that supports something like FS-independent + * buffer_heads with a b_ops-> field. + * + * Copyright (C) 1996, 1997, Olaf Kirch + */ + +#define NFS_NEED_XDR_TYPES +#include +#include +#include +#include +#include +#include +#include +#include + +#define NFSDBG_FACILITY NFSDBG_PAGECACHE + +static void nfs_wback_lock(struct rpc_task *task); +static void nfs_wback_result(struct rpc_task *task); + +/* + * This struct describes a file region to be written. + * It's kind of a pity we have to keep all these lists ourselves, rather + * than sticking an extra pointer into struct page. + */ +struct nfs_wreq { + struct rpc_listitem wb_list; /* linked list of req's */ + struct rpc_task wb_task; /* RPC task */ + struct inode * wb_inode; /* inode referenced */ + struct page * wb_page; /* page to be written */ + unsigned int wb_offset; /* offset within page */ + unsigned int wb_bytes; /* dirty range */ + pid_t wb_pid; /* owner process */ + unsigned short wb_flags; /* status flags */ + + struct nfs_writeargs * wb_args; /* NFS RPC stuff */ + struct nfs_fattr * wb_fattr; /* file attributes */ +}; +#define wb_status wb_task.tk_status + +#define WB_NEXT(req) ((struct nfs_wreq *) ((req)->wb_list.next)) + +/* + * Various flags for wb_flags + */ +#define NFS_WRITE_WANTLOCK 0x0001 /* needs to lock page */ +#define NFS_WRITE_LOCKED 0x0002 /* holds lock on page */ +#define NFS_WRITE_CANCELLED 0x0004 /* has been cancelled */ +#define NFS_WRITE_UNCOMMITTED 0x0008 /* written but uncommitted (NFSv3) */ +#define NFS_WRITE_INVALIDATE 0x0010 /* invalidate after write */ +#define NFS_WRITE_INPROGRESS 0x0020 /* RPC call in progress */ + +#define WB_INPROGRESS(req) ((req)->wb_flags & NFS_WRITE_INPROGRESS) +#define WB_WANTLOCK(req) ((req)->wb_flags & NFS_WRITE_WANTLOCK) +#define WB_HAVELOCK(req) ((req)->wb_flags & NFS_WRITE_LOCKED) +#define WB_CANCELLED(req) ((req)->wb_flags & NFS_WRITE_CANCELLED) +#define WB_UNCOMMITTED(req) ((req)->wb_flags & NFS_WRITE_UNCOMMITTED) +#define WB_INVALIDATE(req) ((req)->wb_flags & NFS_WRITE_INVALIDATE) + +/* + * Cache parameters + */ +#define NFS_WRITEBACK_DELAY (10 * HZ) +#define NFS_WRITEBACK_MAX 64 + +/* + * Limit number of delayed writes + */ +static int nr_write_requests = 0; +static struct rpc_wait_queue write_queue = RPC_INIT_WAITQ("write_chain"); +struct nfs_wreq * nfs_failed_requests = NULL; + +/* Hack for future NFS swap support */ +#ifndef IS_SWAPFILE +# define IS_SWAPFILE(inode) (0) +#endif + +/* + * Unlock a page after writing it + */ +static inline void +nfs_unlock_page(struct page *page) +{ + dprintk("NFS: unlock %ld\n", page->offset); + clear_bit(PG_locked, &page->flags); + wake_up(&page->wait); + +#ifdef CONFIG_NFS_SWAP + /* async swap-out support */ + if (clear_bit(PG_decr_after, &page->flags)) + atomic_dec(&page->count); + if (clear_bit(PG_swap_unlock_after, &page->flags)) + swap_after_unlock_page(page->swap_unlock_entry); +#endif +} + +/* + * Transfer a page lock to a write request waiting for it. + */ +static inline void +transfer_page_lock(struct nfs_wreq *req) +{ + dprintk("NFS: transfer_page_lock\n"); + + req->wb_flags &= ~NFS_WRITE_WANTLOCK; + req->wb_flags |= NFS_WRITE_LOCKED; + rpc_wake_up_task(&req->wb_task); + + dprintk("nfs: wake up task %d (flags %x)\n", + req->wb_task.tk_pid, req->wb_flags); +} + +/* + * Write a page synchronously. + * Offset is the data offset within the page. + */ +static int +nfs_writepage_sync(struct inode *inode, struct page *page, + unsigned long offset, unsigned int count) +{ + struct nfs_fattr fattr; + unsigned int wsize = NFS_SERVER(inode)->wsize; + int result, refresh = 0, written = 0; + u8 *buffer; + + dprintk("NFS: nfs_writepage_sync(%x/%ld %d@%ld)\n", + inode->i_dev, inode->i_ino, + count, page->offset + offset); + + buffer = (u8 *) page_address(page) + offset; + offset += page->offset; + + do { + if (count < wsize && !IS_SWAPFILE(inode)) + wsize = count; + + result = nfs_proc_write(NFS_SERVER(inode), NFS_FH(inode), + offset, wsize, IS_SWAPFILE(inode), + buffer, &fattr); + + if (result < 0) { + /* Must mark the page invalid after I/O error */ + clear_bit(PG_uptodate, &page->flags); + goto io_error; + } + refresh = 1; + buffer += wsize; + offset += wsize; + written += wsize; + count -= wsize; + } while (count); + +io_error: + if (refresh) { + /* See comments in nfs_wback_result */ + if (fattr.size < inode->i_size) + fattr.size = inode->i_size; + /* Solaris 2.5 server seems to send garbled + * fattrs occasionally */ + if (inode->i_ino == fattr.fileid) + nfs_refresh_inode(inode, &fattr); + } + + nfs_unlock_page(page); + return written? written : result; +} + +/* + * Append a writeback request to a list + */ +static inline void +append_write_request(struct nfs_wreq **q, struct nfs_wreq *wreq) +{ + dprintk("NFS: append_write_request(%p, %p)\n", q, wreq); + rpc_append_list(q, wreq); +} + +/* + * Remove a writeback request from a list + */ +static inline void +remove_write_request(struct nfs_wreq **q, struct nfs_wreq *wreq) +{ + dprintk("NFS: remove_write_request(%p, %p)\n", q, wreq); + rpc_remove_list(q, wreq); +} + +/* + * Find a write request for a given page + */ +static inline struct nfs_wreq * +find_write_request(struct inode *inode, struct page *page) +{ + struct nfs_wreq *head, *req; + + dprintk("NFS: find_write_request(%x/%ld, %p)\n", + inode->i_dev, inode->i_ino, page); + if (!(req = head = NFS_WRITEBACK(inode))) + return NULL; + do { + if (req->wb_page == page) + return req; + } while ((req = WB_NEXT(req)) != head); + return NULL; +} + +/* + * Find a failed write request by pid + */ +static inline struct nfs_wreq * +find_failed_request(struct inode *inode, pid_t pid) +{ + struct nfs_wreq *head, *req; + + if (!(req = head = nfs_failed_requests)) + return NULL; + do { + if (req->wb_inode == inode && req->wb_pid == pid) + return req; + } while ((req = WB_NEXT(req)) != head); + return NULL; +} + +/* + * Try to merge adjacent write requests. This works only for requests + * issued by the same user. + */ +static inline int +update_write_request(struct nfs_wreq *req, unsigned first, unsigned bytes) +{ + unsigned rqfirst = req->wb_offset, + rqlast = rqfirst + req->wb_bytes, + last = first + bytes; + + dprintk("nfs: trying to update write request %p\n", req); + + /* Check the credentials associated with this write request. + * If the buffer is owned by the same user, we can happily + * add our data without risking server permission problems. + * Note that I'm not messing around with RPC root override creds + * here, because they're used by swap requests only which + * always write out full pages. */ + if (!rpcauth_matchcred(&req->wb_task, req->wb_task.tk_cred)) { + dprintk("NFS: update failed (cred mismatch)\n"); + return 0; + } + + if (first < rqfirst) + rqfirst = first; + if (rqlast < last) + rqlast = last; + req->wb_offset = rqfirst; + req->wb_bytes = rqlast - rqfirst; + + return 1; +} + +/* + * Create and initialize a writeback request + */ +static inline struct nfs_wreq * +create_write_request(struct inode *inode, struct page *page, + unsigned offset, unsigned bytes) +{ + struct nfs_wreq *wreq; + struct rpc_clnt *clnt = NFS_CLIENT(inode); + struct rpc_task *task; + + dprintk("NFS: create_write_request(%x/%ld, %ld+%d)\n", + inode->i_dev, inode->i_ino, + page->offset + offset, bytes); + + /* FIXME: Enforce hard limit on number of concurrent writes? */ + + wreq = (struct nfs_wreq *) kmalloc(sizeof(*wreq), GFP_USER); + if (!wreq) + return NULL; + memset(wreq, 0, sizeof(*wreq)); + + task = &wreq->wb_task; + rpc_init_task(task, clnt, nfs_wback_result, 0); + task->tk_calldata = wreq; + task->tk_action = nfs_wback_lock; + + rpcauth_lookupcred(task); /* Obtain user creds */ + if (task->tk_status < 0) { + rpc_release_task(task); + kfree(wreq); + return NULL; + } + + /* Put the task on inode's writeback request list. */ + wreq->wb_inode = inode; + wreq->wb_pid = current->pid; + wreq->wb_page = page; + wreq->wb_offset = offset; + wreq->wb_bytes = bytes; + inode->i_count++; + page->count++; + + append_write_request(&NFS_WRITEBACK(inode), wreq); + + if (nr_write_requests++ > NFS_WRITEBACK_MAX*3/4) + rpc_wake_up_next(&write_queue); + + return wreq; +} + +/* + * Schedule a writeback RPC call. + * If the server is congested, don't add to our backlog of queued + * requests but call it synchronously. + * The function returns true if the page has been unlocked as the + * consequence of a synchronous write call. + * + * FIXME: Here we could walk the inode's lock list to see whether the + * page we're currently writing to has been write-locked by the caller. + * If it is, we could schedule an async write request with a long + * delay in order to avoid writing back the page until the lock is + * released. + */ +static inline int +schedule_write_request(struct nfs_wreq *req, int sync) +{ + struct rpc_task *task = &req->wb_task; + struct inode *inode = req->wb_inode; + + if (NFS_CONGESTED(inode) || nr_write_requests >= NFS_WRITEBACK_MAX) + sync = 1; + + if (sync) { + dprintk("NFS: %4d schedule_write_request (sync)\n", + task->tk_pid); + /* Page is already locked */ + req->wb_flags |= NFS_WRITE_LOCKED; + rpc_execute(task); + } else { + dprintk("NFS: %4d schedule_write_request (async)\n", + task->tk_pid); + task->tk_flags |= RPC_TASK_ASYNC; + task->tk_timeout = NFS_WRITEBACK_DELAY; + rpc_sleep_on(&write_queue, task, NULL, NULL); + } + + return sync == 0; +} + +/* + * Wait for request to complete + * This is almost a copy of __wait_on_page + */ +static inline int +wait_on_write_request(struct nfs_wreq *req) +{ + struct wait_queue wait = { current, NULL }; + struct page *page = req->wb_page; + + add_wait_queue(&page->wait, &wait); + page->count++; +repeat: + current->state = TASK_INTERRUPTIBLE; + if (PageLocked(page)) { + schedule(); + goto repeat; + } + remove_wait_queue(&page->wait, &wait); + current->state = TASK_RUNNING; + atomic_dec(&page->count); + return signalled()? -ERESTARTSYS : 0; +} + +/* + * Write a page to the server. This will be used for NFS swapping only + * (for now), and we currently do this synchronously only. + */ +int +nfs_writepage(struct inode *inode, struct page *page) +{ + return nfs_writepage_sync(inode, page, 0, PAGE_SIZE); +} + +/* + * Update and possibly write a cached page of an NFS file. + * The page is already locked when we get here. + * + * XXX: Keep an eye on generic_file_read to make sure it doesn't do bad + * things with a page scheduled for an RPC call (e.g. invalidate it). + */ +int +nfs_updatepage(struct inode *inode, struct page *page, const char *buffer, + unsigned long offset, unsigned int count, int sync) +{ + struct nfs_wreq *req; + int status = 0, page_locked = 1; + u8 *page_addr; + + dprintk("NFS: nfs_updatepage(%x/%ld %d@%ld, sync=%d)\n", + inode->i_dev, inode->i_ino, + count, page->offset+offset, sync); + + page_addr = (u8 *) page_address(page); + + /* If wsize is smaller than page size, update and write + * page synchronously. + */ + if (NFS_SERVER(inode)->wsize < PAGE_SIZE) { + copy_from_user(page_addr + offset, buffer, count); + return nfs_writepage_sync(inode, page, offset, count); + } + + /* + * Try to find a corresponding request on the writeback queue. + * If there is one, we can be sure that this request is not + * yet being processed, because we hold a lock on the page. + * + * If the request was created by us, update it. Otherwise, + * transfer the page lock and flush out the dirty page now. + * After returning, generic_file_write will wait on the + * page and retry the update. + */ + if ((req = find_write_request(inode, page)) != NULL) { + if (update_write_request(req, offset, count)) { + copy_from_user(page_addr + offset, buffer, count); + goto updated; + } + dprintk("NFS: wake up conflicting write request.\n"); + transfer_page_lock(req); + return 0; + } + + /* Create the write request. */ + if (!(req = create_write_request(inode, page, offset, count))) { + status = -ENOBUFS; + goto done; + } + + /* Copy data to page buffer. */ + copy_from_user(page_addr + offset, buffer, count); + + /* Schedule request */ + page_locked = schedule_write_request(req, sync); + +updated: + /* + * If we wrote up to the end of the chunk, transmit request now. + * We should be a bit more intelligent about detecting whether a + * process accesses the file sequentially or not. + */ + if (page_locked && (offset + count >= PAGE_SIZE || sync)) + req->wb_flags |= NFS_WRITE_WANTLOCK; + + /* If the page was written synchronously, return any error that + * may have happened; otherwise return the write count. */ + if (page_locked || (status = nfs_write_error(inode)) >= 0) + status = count; + +done: + /* Unlock page and wake up anyone sleeping on it */ + if (page_locked) { + if (req && WB_WANTLOCK(req)) { + transfer_page_lock(req); + /* rpc_execute(&req->wb_task); */ + if (sync) { + wait_on_write_request(req); + if ((count = nfs_write_error(inode)) < 0) + status = count; + } + } else + nfs_unlock_page(page); + } + + dprintk("NFS: nfs_updatepage returns %d (isize %ld)\n", + status, inode->i_size); + return status; +} + +/* + * Flush out a dirty page. + */ +static inline void +nfs_flush_request(struct nfs_wreq *req) +{ + struct page *page = req->wb_page; + + dprintk("NFS: nfs_flush_request(%x/%ld, @%ld)\n", + page->inode->i_dev, page->inode->i_ino, + page->offset); + + req->wb_flags |= NFS_WRITE_WANTLOCK; + if (!set_bit(PG_locked, &page->flags)) { + transfer_page_lock(req); + } else { + printk(KERN_WARNING "NFS oops in %s: can't lock page!\n", + __FUNCTION__); + rpc_wake_up_task(&req->wb_task); + } +} + +/* + * Flush writeback requests. See nfs_flush_dirty_pages for details. + */ +static struct nfs_wreq * +nfs_flush_pages(struct inode *inode, pid_t pid, off_t offset, off_t len, + int invalidate) +{ + struct nfs_wreq *head, *req, *last = NULL; + off_t rqoffset, rqend, end; + + end = len? offset + len : 0x7fffffffUL; + + req = head = NFS_WRITEBACK(inode); + while (req != NULL) { + dprintk("NFS: %4d nfs_flush inspect %x/%ld @%ld fl %x\n", + req->wb_task.tk_pid, + req->wb_inode->i_dev, req->wb_inode->i_ino, + req->wb_page->offset, req->wb_flags); + if (!WB_INPROGRESS(req)) { + rqoffset = req->wb_page->offset + req->wb_offset; + rqend = rqoffset + req->wb_bytes; + + if (rqoffset < end && offset < rqend + && (pid == 0 || req->wb_pid == pid)) { + if (!WB_HAVELOCK(req)) + nfs_flush_request(req); + last = req; + } + } + if (invalidate) + req->wb_flags |= NFS_WRITE_INVALIDATE; + if ((req = WB_NEXT(req)) == head) + break; + } + + return last; +} + +/* + * Cancel all writeback requests, both pending and in process. + */ +static void +nfs_cancel_dirty(struct inode *inode, pid_t pid) +{ + struct nfs_wreq *head, *req; + + req = head = NFS_WRITEBACK(inode); + while (req != NULL) { + if (req->wb_pid == pid) { + req->wb_flags |= NFS_WRITE_CANCELLED; + rpc_exit(&req->wb_task, 0); + } + if ((req = WB_NEXT(req)) == head) + break; + } +} + +/* + * Flush out all dirty pages belonging to a certain user process and + * maybe wait for the RPC calls to complete. + * + * Another purpose of this function is sync()ing a file range before a + * write lock is released. This is what offset and length are for, even if + * this isn't used by the nlm module yet. + */ +int +nfs_flush_dirty_pages(struct inode *inode, off_t offset, off_t len) +{ + struct nfs_wreq *last = NULL; + + dprintk("NFS: flush_dirty_pages(%x/%ld for pid %d %ld/%ld)\n", + inode->i_dev, inode->i_ino, current->pid, + offset, len); + + if (signalled()) + nfs_cancel_dirty(inode, current->pid); + + while (!signalled()) { + /* Flush all pending writes for this pid and file region */ + last = nfs_flush_pages(inode, current->pid, offset, len, 0); + if (last == NULL) + break; + wait_on_write_request(last); + } + + return signalled()? -ERESTARTSYS : 0; +} + +/* + * Flush out any pending write requests and flag that they be discarded + * after the write is complete. + * + * This function is called from nfs_revalidate_inode just before it calls + * invalidate_inode_pages. After nfs_flush_pages returns, we can be sure + * that all dirty pages are locked, so that invalidate_inode_pages does + * not throw away any dirty pages. + */ +void +nfs_invalidate_pages(struct inode *inode) +{ + dprintk("NFS: nfs_invalidate_pages(%x/%ld)\n", + inode->i_dev, inode->i_ino); + + nfs_flush_pages(inode, 0, 0, 0, 1); +} + +/* + * Cancel any pending write requests after a given offset + * (called from nfs_notify_change). + */ +int +nfs_truncate_dirty_pages(struct inode *inode, unsigned long offset) +{ + struct nfs_wreq *req, *head; + unsigned long rqoffset; + + dprintk("NFS: truncate_dirty_pages(%x/%ld, %ld)\n", + inode->i_dev, inode->i_ino, offset); + + req = head = NFS_WRITEBACK(inode); + while (req != NULL) { + rqoffset = req->wb_page->offset + req->wb_offset; + + if (rqoffset >= offset) { + req->wb_flags |= NFS_WRITE_CANCELLED; + rpc_exit(&req->wb_task, 0); + } else if (rqoffset + req->wb_bytes >= offset) { + req->wb_bytes = offset - rqoffset; + } + if ((req = WB_NEXT(req)) == head) + break; + } + + return 0; +} + +/* + * Check if a previous write operation returned an error + */ +int +nfs_check_error(struct inode *inode) +{ + struct nfs_wreq *req; + int status = 0; + + dprintk("nfs: checking for write error inode %04x/%ld\n", + inode->i_dev, inode->i_ino); + + if (!(req = find_failed_request(inode, current->pid))) + return 0; + + dprintk("nfs: write error %d inode %04x/%ld\n", + req->wb_task.tk_status, inode->i_dev, inode->i_ino); + + status = req->wb_task.tk_status; + remove_write_request(&nfs_failed_requests, req); + iput(req->wb_inode); + kfree(req); + return status; +} + +/* + * The following procedures make up the writeback finite state machinery: + * + * 1. Try to lock the page if not yet locked by us, + * set up the RPC call info, and pass to the call FSM. + */ +static void +nfs_wback_lock(struct rpc_task *task) +{ + struct nfs_wreq *req = (struct nfs_wreq *) task->tk_calldata; + struct page *page = req->wb_page; + struct inode *inode = req->wb_inode; + + dprintk("NFS: %4d nfs_wback_lock (status %d flags %x)\n", + task->tk_pid, task->tk_status, req->wb_flags); + + if (!WB_HAVELOCK(req)) + req->wb_flags |= NFS_WRITE_WANTLOCK; + + if (WB_WANTLOCK(req) && set_bit(PG_locked, &page->flags)) { + dprintk("NFS: page already locked in writeback_lock!\n"); + task->tk_timeout = 2 * HZ; + rpc_sleep_on(&write_queue, task, NULL, NULL); + return; + } + task->tk_status = 0; + req->wb_flags &= ~NFS_WRITE_WANTLOCK; + req->wb_flags |= NFS_WRITE_LOCKED; + + if (req->wb_args == 0) { + size_t size = sizeof(struct nfs_writeargs) + + sizeof(struct nfs_fattr); + void *ptr; + + if (!(ptr = kmalloc(size, GFP_KERNEL))) { + task->tk_timeout = HZ; + rpc_sleep_on(&write_queue, task, NULL, NULL); + return; + } + req->wb_args = (struct nfs_writeargs *) ptr; + req->wb_fattr = (struct nfs_fattr *) (req->wb_args + 1); + } + + /* Setup the task struct for a writeback call */ + req->wb_args->fh = NFS_FH(inode); + req->wb_args->offset = page->offset + req->wb_offset; + req->wb_args->count = req->wb_bytes; + req->wb_args->buffer = (void *) (page_address(page) + req->wb_offset); + + rpc_call_setup(task, NFSPROC_WRITE, req->wb_args, req->wb_fattr, 0); + + req->wb_flags |= NFS_WRITE_INPROGRESS; +} + +/* + * 2. Collect the result + */ +static void +nfs_wback_result(struct rpc_task *task) +{ + struct nfs_wreq *req = (struct nfs_wreq *) task->tk_calldata; + struct inode *inode; + struct page *page; + int status; + + dprintk("NFS: %4d nfs_wback_result (status %d)\n", + task->tk_pid, task->tk_status); + + inode = req->wb_inode; + page = req->wb_page; + status = task->tk_status; + + /* Remove request from writeback list and wake up tasks + * sleeping on it. */ + remove_write_request(&NFS_WRITEBACK(inode), req); + + if (status < 0) { + /* + * An error occurred. Report the error back to the + * application by adding the failed request to the + * inode's error list. + */ + if (find_failed_request(inode, req->wb_pid)) { + status = 0; + } else { + dprintk("NFS: %4d saving write failure code\n", + task->tk_pid); + append_write_request(&nfs_failed_requests, req); + inode->i_count++; + } + clear_bit(PG_uptodate, &page->flags); + } else if (!WB_CANCELLED(req)) { + /* Update attributes as result of writeback. + * Beware: when UDP replies arrive out of order, we + * may end up overwriting a previous, bigger file size. + */ + if (req->wb_fattr->size < inode->i_size) + req->wb_fattr->size = inode->i_size; + /* possible Solaris 2.5 server bug workaround */ + if (inode->i_ino == req->wb_fattr->fileid) + nfs_refresh_inode(inode, req->wb_fattr); + } + + rpc_release_task(task); + + if (WB_INVALIDATE(req)) + clear_bit(PG_uptodate, &page->flags); + if (WB_HAVELOCK(req)) + nfs_unlock_page(page); + + if (req->wb_args) { + kfree(req->wb_args); + req->wb_args = 0; + } + if (status >= 0) + kfree(req); + + free_page(page_address(page)); + iput(inode); + nr_write_requests--; +} diff -u --recursive --new-file v2.1.31/linux/fs/nfsd/Makefile linux/fs/nfsd/Makefile --- v2.1.31/linux/fs/nfsd/Makefile Wed Dec 31 16:00:00 1969 +++ linux/fs/nfsd/Makefile Fri Apr 4 11:06:02 1997 @@ -0,0 +1,20 @@ +# +# Makefile for the linux nfs-filesystem routines. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +O_TARGET := nfsd.o +O_OBJS := nfssvc.o nfsctl.o nfsproc.o nfsfh.o vfs.o \ + export.o auth.o lockd.o nfscache.o nfsxdr.o + +ifdef CONFIG_PROC_FS + O_OBJS += stats.o +endif + +M_OBJS := $(O_TARGET) + +include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.1.31/linux/fs/nfsd/auth.c linux/fs/nfsd/auth.c --- v2.1.31/linux/fs/nfsd/auth.c Wed Dec 31 16:00:00 1969 +++ linux/fs/nfsd/auth.c Fri Apr 4 11:06:02 1997 @@ -0,0 +1,47 @@ +/* + * linux/fs/nfsd/auth.c + * + * Copyright (C) 1995, 1996 Olaf Kirch + */ + +#include +#include +#include +#include +#include + +void +nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp) +{ + struct svc_cred *cred = &rqstp->rq_cred; + int i; + + if (rqstp->rq_userset) + return; + + if (exp->ex_flags & NFSEXP_ALLSQUASH) { + cred->cr_uid = exp->ex_anon_uid; + cred->cr_gid = exp->ex_anon_gid; + cred->cr_groups[0] = NOGROUP; + } else if (exp->ex_flags & NFSEXP_ROOTSQUASH) { + if (!cred->cr_uid) + cred->cr_uid = exp->ex_anon_uid; + if (!cred->cr_gid) + cred->cr_gid = exp->ex_anon_gid; + for (i = 0; i < NGROUPS; i++) + if (!cred->cr_groups[i]) + cred->cr_groups[i] = exp->ex_anon_gid; + } + + if (cred->cr_uid != (uid_t) -1) + current->fsuid = cred->cr_uid; + else + current->fsuid = exp->ex_anon_uid; + if (cred->cr_gid != (gid_t) -1) + current->fsgid = cred->cr_gid; + else + current->fsgid = exp->ex_anon_gid; + for (i = 0; i < NGROUPS; i++) + current->groups[i] = cred->cr_groups[i]; + rqstp->rq_userset = 1; +} diff -u --recursive --new-file v2.1.31/linux/fs/nfsd/export.c linux/fs/nfsd/export.c --- v2.1.31/linux/fs/nfsd/export.c Wed Dec 31 16:00:00 1969 +++ linux/fs/nfsd/export.c Fri Apr 4 11:06:02 1997 @@ -0,0 +1,677 @@ +/* + * linux/fs/nfsd/export.c + * + * NFS exporting and validation. + * + * We maintain a list of clients, each of which has a list of + * exports. To export an fs to a given client, you first have + * to create the client entry with NFSCTL_ADDCLIENT, which + * creates a client control block and adds it to the hash + * table. Then, you call NFSCTL_EXPORT for each fs. + * + * You cannot currently read the export information from the + * kernel. It would be nice to have a /proc file though. + * + * Copyright (C) 1995, 1996 Olaf Kirch, + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +typedef struct svc_client svc_client; +typedef struct svc_export svc_export; + +static svc_export * exp_find(svc_client *clp, dev_t dev); +static svc_export * exp_parent(svc_client *clp, dev_t dev); +static void exp_unexport_all(svc_client *clp); +static void exp_do_unexport(svc_export *unexp); +static svc_client * exp_getclientbyname(char *name); +static void exp_freeclient(svc_client *clp); +static void exp_unhashclient(svc_client *clp); +static int exp_verify_string(char *cp, int max); +struct inode * exp_lnamei(char *pathname, int *errp); + +#define NFSDDBG_FACILITY NFSDDBG_EXPORT +#define CLIENT_HASHBITS 6 +#define CLIENT_HASHMAX (1 << CLIENT_HASHBITS) +#define CLIENT_HASHMASK (CLIENT_HASHMAX - 1) +#define CLIENT_HASH(a) \ + ((((a)>>24) ^ ((a)>>16) ^ ((a)>>8) ^(a)) & CLIENT_HASHMASK) +#define EXPORT_HASH(dev) ((dev) & (NFSCLNT_EXPMAX - 1)) + +struct svc_clnthash { + struct svc_clnthash * h_next; + struct in_addr h_addr; + struct svc_client * h_client; +}; +static struct svc_clnthash * clnt_hash[CLIENT_HASHMAX]; +static svc_client * clients = NULL; +static int initialized = 0; + +static int hash_lock = 0; +static int want_lock = 0; +static int hash_count = 0; +static struct wait_queue * hash_wait = NULL; + +#define READLOCK 0 +#define WRITELOCK 1 + +/* + * Find the export entry matching xdev/xino. + */ +static inline svc_export * +exp_find(svc_client *clp, dev_t dev) +{ + svc_export * exp; + + exp = clp->cl_export[EXPORT_HASH(dev)]; + while (exp && exp->ex_dev != dev) + exp = exp->ex_next; + return exp; +} + +svc_export * +exp_get(svc_client *clp, dev_t dev, ino_t ino) +{ + svc_export * exp; + + if (!clp) + return NULL; + exp = exp_find(clp, dev); + return (exp && exp->ex_ino == ino)? exp : NULL; +} + +/* + * Find the parent export entry for a given fs. This function is used + * only by the export syscall to keep the export tree consistent. + * nfsd_parentdev(dev) returns the device on which dev is mounted. + */ +static svc_export * +exp_parent(svc_client *clp, dev_t dev) +{ + svc_export *exp; + dev_t xdev = dev; + + do { + exp = exp_find(clp, xdev); + if (exp) + return exp; + } while (nfsd_parentdev(&xdev)); + + return NULL; +} + +/* + * Export a file system. + */ +int +exp_export(struct nfsctl_export *nxp) +{ + svc_client *clp; + svc_export *exp, *parent; + svc_export **head; + struct inode *inode = NULL; + int i, err; + dev_t dev; + ino_t ino; + + /* Consistency check */ + if (!exp_verify_string(nxp->ex_path, NFS_MAXPATHLEN) || + !exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX)) + return -EINVAL; + + dprintk("exp_export called for %s:%s (%x/%ld fl %x).\n", + nxp->ex_client, nxp->ex_path, + nxp->ex_dev, nxp->ex_ino, nxp->ex_flags); + dev = nxp->ex_dev; + ino = nxp->ex_ino; + + /* Try to lock the export table for update */ + if ((err = exp_writelock()) < 0) + return err; + + /* Look up client info */ + if (!(clp = exp_getclientbyname(nxp->ex_client))) { + err = -EINVAL; + goto finish; + } + + /* + * If there's already an export for this file, assume this + * is just a flag update. + */ + if ((exp = exp_find(clp, dev)) != NULL) { + /* Ensure there's only one export per FS. */ + if (exp->ex_ino != ino) { + err = -EPERM; + } else { + exp->ex_flags = nxp->ex_flags; + exp->ex_anon_uid = nxp->ex_anon_uid; + exp->ex_anon_gid = nxp->ex_anon_gid; + err = 0; + } + goto finish; + } + + /* Look up the inode */ + if (!(inode = nfsd_iget(nxp->ex_dev, nxp->ex_ino))) { + err = -EINVAL; + goto finish; + } + + /* We currently export only dirs. */ + if (!S_ISDIR(inode->i_mode)) { + err = -ENOTDIR; + goto finish; + } + + /* If this is a sub-export, must be root of FS */ + if ((parent = exp_parent(clp, dev)) != NULL) { + struct super_block *sb; + + if ((sb = inode->i_sb) && (inode != sb->s_mounted)) { + err = -EINVAL; + goto finish; + } + } + + if (!(exp = kmalloc(sizeof(*exp), GFP_USER))) { + err = -ENOMEM; + goto finish; + } + dprintk("nfsd: created export entry %p for client %p\n", exp, clp); + + strcpy(exp->ex_path, nxp->ex_path); + exp->ex_client = clp; + exp->ex_parent = parent; + exp->ex_inode = inode; + exp->ex_flags = nxp->ex_flags; + exp->ex_dev = dev; + exp->ex_ino = ino; + exp->ex_anon_uid = nxp->ex_anon_uid; + exp->ex_anon_gid = nxp->ex_anon_gid; + + /* Update parent pointers of all exports */ + if (parent) { + for (i = 0; i < NFSCLNT_EXPMAX; i++) { + svc_export *temp = clp->cl_export[i]; + + while (temp) { + if (temp->ex_parent == parent) + temp->ex_parent = exp; + temp = temp->ex_next; + } + } + } + + head = clp->cl_export + EXPORT_HASH(dev); + exp->ex_next = *head; + *head = exp; + + err = 0; + +finish: + /* Release inode */ + if (err < 0 && inode) + iput(inode); + /* Unlock hashtable */ + exp_unlock(); + return err; +} + +/* + * Unexport a file system. The export entry has already + * been removed from the client's list of exported fs's. + */ +static void +exp_do_unexport(svc_export *unexp) +{ + svc_export *exp; + svc_client *clp; + struct inode *inode; + int i; + + /* Update parent pointers. */ + clp = unexp->ex_client; + for (i = 0; i < NFSCLNT_EXPMAX; i++) { + for (exp = clp->cl_export[i]; exp; exp = exp->ex_next) + if (exp->ex_parent == unexp) + exp->ex_parent = unexp->ex_parent; + } + + inode = unexp->ex_inode; + if (unexp->ex_dev != inode->i_dev || unexp->ex_ino != inode->i_ino) + printk(KERN_WARNING "nfsd: bad inode in unexport!\n"); + else + iput(unexp->ex_inode); + + kfree(unexp); +} + +/* + * Revoke all exports for a given client. + * This may look very awkward, but we have to do it this way in order + * to avoid race conditions (aka mind the parent pointer). + */ +static void +exp_unexport_all(svc_client *clp) +{ + svc_export *exp; + int i; + + dprintk("unexporting all fs's for clnt %p\n", clp); + for (i = 0; i < NFSCLNT_EXPMAX; i++) { + exp = clp->cl_export[i]; + clp->cl_export[i] = NULL; + while (exp) { + svc_export *next = exp->ex_next; + exp_do_unexport(exp); + exp = next; + } + } +} + +/* + * unexport syscall. + */ +int +exp_unexport(struct nfsctl_export *nxp) +{ + svc_client *clp; + svc_export **expp, *exp = NULL; + int err; + + /* Consistency check */ + if (!exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX)) + return -EINVAL; + + if ((err = exp_writelock()) < 0) + return err; + + err = -EINVAL; + if ((clp = exp_getclientbyname(nxp->ex_client)) != NULL) { + expp = clp->cl_export + EXPORT_HASH(nxp->ex_dev); + while ((exp = *expp) != NULL) { + if (exp->ex_dev == nxp->ex_dev) { + if (exp->ex_ino != nxp->ex_ino) + break; + *expp = exp->ex_next; + exp_do_unexport(exp); + err = 0; + break; + } + expp = &(exp->ex_next); + } + } + + exp_unlock(); + return err; +} + +/* + * Obtain the root fh on behalf of a client. + * This could be done in user space, but I feel that it adds some safety + * since its harder to fool a kernel module than a user space program. + */ +int +exp_rootfh(struct svc_client *clp, dev_t dev, ino_t ino, struct knfs_fh *f) +{ + struct svc_export *exp = NULL; + struct svc_fh fh; + + dprintk("nfsd: exp_rootfh(%s:%x/%ld)\n", clp->cl_ident, dev, ino); + + if (!(exp = exp_get(clp, dev, ino))) + return -EPERM; + exp->ex_inode->i_count++; + fh_compose(&fh, exp, exp->ex_inode); + memcpy(f, &fh.fh_handle, sizeof(struct knfs_fh)); + fh_put(&fh); + + return 0; +} + +/* + * Hashtable locking. Write locks are placed only by user processes + * wanting to modify export information. + */ +void +exp_readlock(void) +{ + while (hash_lock || want_lock) + sleep_on(&hash_wait); + hash_count++; +} + +int +exp_writelock(void) +{ + want_lock++; + while (hash_count || hash_lock) + interruptible_sleep_on(&hash_wait); + want_lock--; + if (current->signal & ~current->blocked) + return -EINTR; + hash_lock = 1; + return 0; +} + +void +exp_unlock(void) +{ + if (hash_count) + hash_count--; + else + hash_lock = 0; + wake_up(&hash_wait); +} + +/* + * Find a valid client given an inet address. We always move the most + * recently used client to the front of the hash chain to speed up + * future lookups. + * Locking against other processes is the responsibility of the caller. + */ +struct svc_client * +exp_getclient(struct sockaddr_in *sin) +{ + struct svc_clnthash **hp, **head, *tmp; + unsigned long addr = sin->sin_addr.s_addr; + + if (!initialized) + return NULL; + + head = &clnt_hash[CLIENT_HASH(addr)]; + + for (hp = head; (tmp = *hp) != NULL; hp = &(tmp->h_next)) { + if (tmp->h_addr.s_addr == addr) { + /* Move client to the front */ + if (head != hp) { + *hp = tmp->h_next; + tmp->h_next = *head; + *head = tmp; + } + + return tmp->h_client; + } + } + + return NULL; +} + +/* + * Find a client given its identifier. + */ +static svc_client * +exp_getclientbyname(char *ident) +{ + svc_client * clp; + + for (clp = clients; clp; clp = clp->cl_next) { + if (!strcmp(clp->cl_ident, ident)) + return clp; + } + return NULL; +} + +/* + * Add or modify a client. + * Change requests may involve the list of host addresses. The list of + * exports and possibly existing uid maps are left untouched. + */ +int +exp_addclient(struct nfsctl_client *ncp) +{ + struct svc_clnthash * ch[NFSCLNT_ADDRMAX]; + svc_client * clp; + int i, err, change = 0, ilen; + + /* First, consistency check. */ + if (!(ilen = exp_verify_string(ncp->cl_ident, NFSCLNT_IDMAX))) + return -EINVAL; + if (ncp->cl_naddr > NFSCLNT_ADDRMAX) + return -EINVAL; + + /* Lock the hashtable */ + if ((err = exp_writelock()) < 0) + return err; + + /* First check if this is a change request for a client. */ + for (clp = clients; clp; clp = clp->cl_next) + if (!strcmp(clp->cl_ident, ncp->cl_ident)) + break; + + if (clp) { + change = 1; + } else { + if (!(clp = kmalloc(sizeof(*clp), GFP_KERNEL))) { + exp_unlock(); + return -ENOMEM; + } + memset(clp, 0, sizeof(*clp)); + + dprintk("created client %s (%p)\n", ncp->cl_ident, clp); + + strcpy(clp->cl_ident, ncp->cl_ident); + clp->cl_idlen = ilen; + } + + /* Allocate hash buckets */ + for (i = 0; i < ncp->cl_naddr; i++) { + if (!(ch[i] = kmalloc(GFP_KERNEL, sizeof(ch[0])))) { + while (i--) + kfree(ch[i]); + if (!change) + kfree(clp); + exp_unlock(); + return -ENOMEM; + } + } + + /* Copy addresses. */ + for (i = 0; i < ncp->cl_naddr; i++) { + clp->cl_addr[i] = ncp->cl_addrlist[i]; + } + clp->cl_naddr = ncp->cl_naddr; + + /* Remove old client hash entries. */ + if (change) + exp_unhashclient(clp); + + /* Insert client into hashtable. */ + for (i = 0; i < ncp->cl_naddr; i++) { + struct in_addr addr = clp->cl_addr[i]; + int hash; + + hash = CLIENT_HASH(addr.s_addr); + ch[i]->h_client = clp; + ch[i]->h_addr = addr; + ch[i]->h_next = clnt_hash[hash]; + clnt_hash[hash] = ch[i]; + } + + if (!change) { + clp->cl_next = clients; + clients = clp; + } + + exp_unlock(); + return 0; +} + +/* + * Delete a client given an identifier. + */ +int +exp_delclient(struct nfsctl_client *ncp) +{ + svc_client **clpp, *clp; + int err; + + if (!exp_verify_string(ncp->cl_ident, NFSCLNT_IDMAX)) + return -EINVAL; + + /* Lock the hashtable */ + if ((err = exp_writelock()) < 0) + return err; + + for (clpp = &clients; (clp = *clpp); clpp = &(clp->cl_next)) + if (!strcmp(ncp->cl_ident, clp->cl_ident)) + break; + + if (!clp) { + exp_unlock(); + return -EINVAL; + } + *clpp = clp->cl_next; + exp_freeclient(clp); + + exp_unlock(); + return 0; +} + +/* + * Free a client. The caller has already removed it from the client list. + */ +static void +exp_freeclient(svc_client *clp) +{ + exp_unhashclient(clp); + + /* umap_free(&(clp->cl_umap)); */ + exp_unexport_all(clp); + nfsd_lockd_unexport(clp); + kfree (clp); +} + +/* + * Remove client from hashtable. We first collect all hashtable + * entries and free them in one go. + * The hash table must be writelocked by the caller. + */ +static void +exp_unhashclient(svc_client *clp) +{ + struct svc_clnthash **hpp, *hp, *ch[NFSCLNT_ADDRMAX]; + int i, count, err; + +again: + err = 0; + for (i = 0, count = 0; i < CLIENT_HASHMAX && !err; i++) { + hpp = clnt_hash + i; + while ((hp = *hpp) && !err) { + if (hp->h_client == clp) { + *hpp = hp->h_next; + ch[count++] = hp; + err = (count >= NFSCLNT_ADDRMAX); + } else { + hpp = &(hp->h_next); + } + } + } + if (count != clp->cl_naddr) + printk(KERN_WARNING "nfsd: bad address count in freeclient!\n"); + if (err) + goto again; + for (i = 0; i < count; i++) + kfree (ch[i]); +} + +/* + * Lockd is shutting down and tells us to unregister all clients + */ +void +exp_nlmdetach(void) +{ + struct svc_client *clp; + + for (clp = clients; clp; clp = clp->cl_next) + nfsd_lockd_unexport(clp); +} + +/* + * Verify that string is non-empty and does not exceed max length. + */ +static int +exp_verify_string(char *cp, int max) +{ + int i; + + for (i = 0; i < max; i++) + if (!cp[i]) + return i; + cp[i] = 0; + printk(KERN_NOTICE "nfsd: couldn't validate string %s\n", cp); + return 0; +} + +#if 0 +/* + * Get the inode associated with a pathname. Used by exp_export. + */ +struct inode * +exp_lnamei(char *pathname, int *errp) +{ + struct inode *inode; + unsigned long oldfs; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + *errp = lnamei(pathname, &inode); + set_fs(oldfs); + + return inode; +} +#endif + +/* + * Initialize the exports module. + */ +void +nfsd_export_init(void) +{ + int i; + + dprintk("nfsd: initializing export module.\n"); + if (initialized) + return; + for (i = 0; i < CLIENT_HASHMAX; i++) + clnt_hash[i] = NULL; + clients = NULL; + + initialized = 1; + return; +} + +/* + * Shutdown the exports module. + */ +void +nfsd_export_shutdown(void) +{ + int i; + + dprintk("nfsd: shutting down export module.\n"); + if (!initialized) + return; + if (exp_writelock() < 0) { + printk(KERN_WARNING "Weird: hashtable locked in exp_shutdown"); + return; + } + for (i = 0; i < CLIENT_HASHMAX; i++) { + while (clnt_hash[i]) + exp_freeclient(clnt_hash[i]->h_client); + } + exp_unlock(); + dprintk("nfsd: export shutdown complete.\n"); + + return; +} diff -u --recursive --new-file v2.1.31/linux/fs/nfsd/lockd.c linux/fs/nfsd/lockd.c --- v2.1.31/linux/fs/nfsd/lockd.c Wed Dec 31 16:00:00 1969 +++ linux/fs/nfsd/lockd.c Fri Apr 4 11:06:02 1997 @@ -0,0 +1,70 @@ +/* + * linux/fs/nfsd/lockd.c + * + * This file contains all the stubs needed when communicating with lockd. + * This level of indirection is necessary so we can run nfsd+lockd without + * requiring the nfs client to be compiled in/loaded, and vice versa. + * + * Copyright (C) 1996, Olaf Kirch + */ + +#include +#include +#include +#include +#include + +#define NFSDDBG_FACILITY NFSDDBG_LOCKD + +static u32 +nlm_fopen(struct svc_rqst *rqstp, struct knfs_fh *f, struct file *filp) +{ + struct svc_fh fh; + u32 nfserr; + + fh.fh_handle = *f; + fh.fh_export = NULL; + fh.fh_inode = NULL; + + nfserr = nfsd_open(rqstp, &fh, S_IFREG, 0, filp); + fh_put(&fh); + return nfserr; +} + +static void +nlm_fclose(struct file *filp) +{ + nfsd_close(filp); +} + +struct nlmsvc_binding nfsd_nlm_ops = { + exp_readlock, /* lock export table for reading */ + exp_unlock, /* unlock export table */ + exp_getclient, /* look up NFS client */ + nlm_fopen, /* open file for locking */ + nlm_fclose, /* close file */ + exp_nlmdetach, /* lockd shutdown notification */ +}; + +/* + * When removing an NFS client entry, notify lockd that it is gone. + * FIXME: We should do the same when unexporting an NFS volume. + */ +void +nfsd_lockd_unexport(struct svc_client *clnt) +{ + nlmsvc_invalidate_client(clnt); +} + +void +nfsd_lockd_init(void) +{ + dprintk("nfsd: initializing lockd\n"); + nlmsvc_ops = &nfsd_nlm_ops; +} + +void +nfsd_lockd_shutdown(void) +{ + nlmsvc_ops = NULL; +} diff -u --recursive --new-file v2.1.31/linux/fs/nfsd/nfs3proc.c linux/fs/nfsd/nfs3proc.c --- v2.1.31/linux/fs/nfsd/nfs3proc.c Wed Dec 31 16:00:00 1969 +++ linux/fs/nfsd/nfs3proc.c Fri Apr 4 11:06:02 1997 @@ -0,0 +1,529 @@ +/* + * linux/fs/nfsd/nfs3proc.c + * + * Process version 3 NFS requests. + * + * Copyright (C) 1996 Olaf Kirch + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +typedef struct svc_rqst svc_rqst; +typedef struct svc_buf svc_buf; + +#define NFSDDBG_FACILITY NFSDDBG_PROC + +#define RETURN(st) { resp->status = (st); return (st); } + +static void +svcbuf_reserve(struct svc_buf *buf, u32 **ptr, int *len, int nr) +{ + *ptr = buf->buf + nr; + *len = buf->buflen - buf->len - nr; +} + +static int +nfsd3_proc_null(struct svc_rqst *rqstp, void *argp, void *resp) +{ + return nfs_ok; +} + +/* + * Get a file's attributes + */ +static int +nfsd3_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle *argp, + struct nfsd3_attrstat *resp) +{ + int nfserr; + + dprintk("nfsd: GETATTR %x/%ld\n", + SVCFH_DEV(&argp->fh), + SVCFH_INO(&argp->fh)); + + resp->fh = argp->fh; + nfserr = fh_lookup(rqstp, &resp->fh, 0, MAY_NOP); + RETURN(nfserr); +} + +/* + * Set a file's attributes + */ +static int +nfsd3_proc_setattr(struct svc_rqst *rqstp, struct nfsd3_sattrargs *argp, + struct nfsd3_attrstat *resp) +{ + int nfserr; + + dprintk("nfsd: SETATTR %x/%ld\n", + SVCFH_DEV(&argp->fh), + SVCFH_INO(&argp->fh)); + + resp->fh = argp->fh; + nfserr = nfsd_setattr(rqstp, &resp->fh, &argp->attrs); + RETURN(nfserr); +} + +/* + * Look up a path name component + */ +static int +nfsd3_proc_lookup(struct svc_rqst *rqstp, struct nfsd3_diropargs *argp, + struct nfsd3_lookupres *resp) +{ + int nfserr; + + dprintk("nfsd: LOOKUP %x/%ld %s\n", + SVCFH_DEV(&argp->fh), + SVCFH_INO(&argp->fh), + argp->name); + + resp->dirfh = argp->fh; + nfserr = nfsd_lookup(rqstp, &resp->dirfh, + argp->name, + argp->len, + &resp->fh); + RETURN(nfserr); +} + +/* + * Check file access + */ +static int +nfsd3_proc_access(struct svc_rqst *rqstp, struct nfsd_fhandle *argp, + struct nfsd3_accessres *resp) +{ + /* to be done */ + resp->fh = argp->fh; + return nfserr_notsupp; +} + +/* + * Read a symlink. + */ +static int +nfsd3_proc_readlink(struct svc_rqst *rqstp, struct nfsd_fhandle *argp, + struct nfsd3_readlinkres *resp) +{ + u32 *path; + int dummy, nfserr; + + dprintk("nfsd: READLINK %x/%ld\n", + SVCFH_DEV(&argp->fh), + SVCFH_INO(&argp->fh)); + + /* Reserve room for status, post_op_attr, and path length */ + svcbuf_reserve(&rqstp->rq_resbuf, &path, &dummy, 1 + 22 + 1); + + /* Read the symlink. */ + resp->len = NFS3_MAXPATHLEN; + nfserr = nfsd_readlink(rqstp, &argp->fh, (char *) path, &resp->len); + RETURN(nfserr); +} + +/* + * Read a portion of a file. + */ +static int +nfsd3_proc_read(struct svc_rqst *rqstp, struct nfsd3_readargs *argp, + struct nfsd3_readres *resp) +{ + u32 * buffer; + int nfserr, avail; + + dprintk("nfsd: READ %x/%ld %lu bytes at %lu\n", + SVCFH_DEV(&argp->fh), + SVCFH_INO(&argp->fh), + (unsigned long) argp->count, + (unsigned long) argp->offset); + + /* Obtain buffer pointer for payload. + * 1 (status) + 22 (post_op_attr) + 1 (count) + 1 (eof) + * + 1 (xdr opaque byte count) = 26 + */ + svcbuf_reserve(&rqstp->rq_resbuf, &buffer, &avail, 26); + + if ((avail << 2) < argp->count) { + printk(KERN_NOTICE + "oversized read request from %08lx:%d (%d bytes)\n", + ntohl(rqstp->rq_addr.sin_addr.s_addr), + ntohs(rqstp->rq_addr.sin_port), + argp->count); + argp->count = avail; + } + + resp->count = argp->count; + resp->fh = argp->fh; + nfserr = nfsd_read(rqstp, &resp->fh, + argp->offset, + (char *) buffer, + &resp->count); + + RETURN(nfserr); +} + +/* + * Write data to a file + */ +static int +nfsd3_proc_write(struct svc_rqst *rqstp, struct nfsd3_writeargs *argp, + struct nfsd3_writeres *resp) +{ + int nfserr; + + dprintk("nfsd: WRITE %x/%ld %d bytes at %ld\n", + SVCFH_DEV(&argp->fh), + SVCFH_INO(&argp->fh), + argp->len, + (unsigned long) argp->offset); + + resp->fh = argp->fh; + nfserr = nfsd_write(rqstp, &resp->fh, + argp->offset, + argp->data, + argp->len, + argp->stable); + resp->committed = argp->stable; + RETURN(nfserr); +} + +/* + * With NFSv3, CREATE processing is a lot easier than with NFSv2. + * At least in theory; we'll see how it fares in practice when the + * first reports about SunOS compatibility problems start to pour in... + */ +static int +nfsd3_proc_create(struct svc_rqst *rqstp, struct nfsd3_createargs *argp, + struct nfsd3_createres *resp) +{ + svc_fh *dirfhp, *newfhp = NULL; + struct iattr *attr; + int mode; + u32 nfserr; + + dprintk("nfsd: CREATE %x/%ld %s\n", + SVCFH_DEV(&argp->fh), + SVCFH_INO(&argp->fh), + argp->name); + + dirfhp = fh_copy(&resp->dirfh, &argp->fh); + newfhp = fh_init(&resp->fh); + attr = &argp->attrs; + + /* Get the directory inode */ + nfserr = fh_lookup(rqstp, dirfhp, S_IFDIR, MAY_CREATE); + if (nfserr) + RETURN(nfserr); + + /* Unfudge the mode bits */ + attr->ia_mode &= ~S_IFMT; + if (!(attr->ia_valid & ATTR_MODE)) { + attr->ia_valid |= ATTR_MODE; + attr->ia_mode = S_IFREG; + } + mode = attr->ia_mode & ~S_IFMT; + + /* Now create the file and set attributes */ + nfserr = nfsd_create(rqstp, dirfhp, argp->name, argp->len, + attr, S_IFREG, 0, newfhp, + argp->createmode); + + RETURN(nfserr); +} + +static int +nfsd3_proc_remove(struct svc_rqst *rqstp, struct nfsd3_diropargs *argp, + struct nfsd3_attrstat *resp) +{ + int nfserr; + + dprintk("nfsd: REMOVE %x/%ld %s\n", + SVCFH_DEV(&argp->fh), + SVCFH_INO(&argp->fh), + argp->name); + + fh_copy(&resp->fh, &argp->fh); + + /* Unlink. -S_IFDIR means file must not be a directory */ + nfserr = nfsd_unlink(rqstp, &resp->fh, -S_IFDIR, + argp->name, argp->len); + RETURN(nfserr); +} + +static int +nfsd3_proc_rename(struct svc_rqst *rqstp, struct nfsd3_renameargs *argp, + void *resp) +{ + int nfserr; + + dprintk("nfsd: RENAME %x/%ld %s -> %x/%ld %s\n", + SVCFH_DEV(&argp->ffh), + SVCFH_INO(&argp->ffh), + argp->fname, + SVCFH_DEV(&argp->tfh), + SVCFH_INO(&argp->tfh), + argp->tname); + + nfserr = nfsd_rename(rqstp, &argp->ffh, argp->fname, argp->flen, + &argp->tfh, argp->tname, argp->tlen); + fh_put(&argp->ffh); + fh_put(&argp->tfh); + RETURN(nfserr); +} + +static int +nfsd3_proc_link(struct svc_rqst *rqstp, struct nfsd3_linkargs *argp, + void *resp) +{ + int nfserr; + + dprintk("nfsd: LINK %x/%ld -> %x/%ld %s\n", + SVCFH_DEV(&argp->ffh), + SVCFH_INO(&argp->ffh), + SVCFH_DEV(&argp->tfh), + SVCFH_INO(&argp->tfh), + argp->tname); + + nfserr = nfsd_link(rqstp, &argp->tfh, argp->tname, argp->tlen, + &argp->ffh); + fh_put(&argp->ffh); + fh_put(&argp->tfh); + RETURN(nfserr); +} + +static int +nfsd3_proc_symlink(struct svc_rqst *rqstp, struct nfsd3_symlinkargs *argp, + void *resp) +{ + struct svc_fh newfh; + int nfserr; + + dprintk("nfsd: SYMLINK %x/%ld %s -> %s\n", + SVCFH_DEV(&argp->ffh), + SVCFH_INO(&argp->ffh), + argp->fname, argp->tname); + + memset(&newfh, 0, sizeof(newfh)); + + /* + * Create the link, look up new file and set attrs. + */ + nfserr = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen, + argp->tname, argp->tlen, + &newfh); + if (nfserr) + nfserr = nfsd_setattr(rqstp, &newfh, &argp->attrs); + + fh_put(&argp->ffh); + fh_put(&newfh); + RETURN(nfserr); +} + +/* + * Make directory. This operation is not idempotent. + */ +static int +nfsd3_proc_mkdir(struct svc_rqst *rqstp, struct nfsd3_createargs *argp, + struct nfsd3_diropres *resp) +{ + int nfserr; + + dprintk("nfsd: MKDIR %x/%ld %s\n", + SVCFH_DEV(&argp->fh), + SVCFH_INO(&argp->fh), + argp->name); + + nfserr = nfsd_create(rqstp, &argp->fh, argp->name, argp->len, + &argp->attrs, S_IFDIR, 0, &resp->fh); + fh_put(&argp->fh); + RETURN(nfserr); +} + +/* + * Remove a directory + */ +static int +nfsd3_proc_rmdir(struct svc_rqst *rqstp, struct nfsd3_diropargs *argp, + void *resp) +{ + int nfserr; + + dprintk("nfsd: RMDIR %x/%ld %s\n", + SVCFH_DEV(&argp->fh), + SVCFH_INO(&argp->fh), + argp->name); + + nfserr = nfsd_unlink(rqstp, &argp->fh, S_IFDIR, argp->name, argp->len); + fh_put(&argp->fh); + RETURN(nfserr); +} + +/* + * Read a portion of a directory. + */ +static int +nfsd3_proc_readdir(struct svc_rqst *rqstp, struct nfsd3_readdirargs *argp, + struct nfsd3_readdirres *resp) +{ + u32 * buffer; + int nfserr, count; + + dprintk("nfsd: READDIR %x/%ld %d bytes at %d\n", + SVCFH_DEV(&argp->fh), + SVCFH_INO(&argp->fh), + argp->count, argp->cookie); + + /* Reserve buffer space for status */ + svcbuf_reserve(&rqstp->rq_resbuf, &buffer, &count, 1); + + /* Make sure we've room for the NULL ptr & eof flag, and shrink to + * client read size */ + if ((count -= 8) > argp->count) + count = argp->count; + + /* Read directory and encode entries on the fly */ + nfserr = nfsd_readdir(rqstp, &argp->fh, (loff_t) argp->cookie, + nfssvc_encode_entry, + buffer, &count); + resp->count = count; + + fh_put(&argp->fh); + RETURN(nfserr); +} + +/* + * Get file system info + */ +static int +nfsd3_proc_statfs(struct svc_rqst * rqstp, struct nfsd_fhandle *argp, + struct nfsd3_statfsres *resp) +{ + int nfserr; + + dprintk("nfsd: STATFS %x/%ld\n", + SVCFH_DEV(&argp->fh), + SVCFH_INO(&argp->fh)); + + nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats); + fh_put(&argp->fh); + RETURN(nfserr); +} + +/* + * NFSv2 Server procedures. + * Only the results of non-idempotent operations are cached. + */ +#define nfsd3_proc_none NULL +#define nfssvc_encode_void NULL +#define nfssvc_decode_void NULL +#define nfssvc_release_void NULL +struct nfsd3_void { int dummy; }; + +#define PROC(name, argt, rest, relt, cache) \ + { (svc_procfunc) nfsd3_proc_##name, \ + (kxdrproc_t) nfssvc_decode_##argt, \ + (kxdrproc_t) nfssvc_encode_##rest, \ + (kxdrproc_t) nfssvc_release_##relt, \ + sizeof(struct nfsd3_##argt), \ + sizeof(struct nfsd3_##rest), \ + 0, \ + cache \ + } +struct svc_procedure nfsd3_procedures2[18] = { + PROC(null, void, void, void, RC_NOCACHE), + PROC(getattr, fhandle, attrstat, fhandle, RC_NOCACHE), + PROC(setattr, sattrargs, attrstat, fhandle, RC_REPLBUFF), + PROC(none, void, void, void, RC_NOCACHE), + PROC(lookup, diropargs, diropres, fhandle, RC_NOCACHE), + PROC(readlink, fhandle, readlinkres, void, RC_NOCACHE), + PROC(read, readargs, readres, fhandle, RC_NOCACHE), + PROC(none, void, void, void, RC_NOCACHE), + PROC(write, writeargs, attrstat, fhandle, RC_REPLBUFF), + PROC(create, createargs, diropres, fhandle, RC_REPLBUFF), + PROC(remove, diropargs, void, void, RC_REPLSTAT), + PROC(rename, renameargs, void, void, RC_REPLSTAT), + PROC(link, linkargs, void, void, RC_REPLSTAT), + PROC(symlink, symlinkargs, void, void, RC_REPLSTAT), + PROC(mkdir, createargs, diropres, fhandle, RC_REPLBUFF), + PROC(rmdir, diropargs, void, void, RC_REPLSTAT), + PROC(readdir, readdirargs, readdirres, void, RC_REPLSTAT), + PROC(statfs, fhandle, statfsres, void, RC_NOCACHE), +}; + + +/* + * Map errnos to NFS errnos. + */ +int +nfserrno (int errno) +{ + static struct { + int nfserr; + int syserr; + } nfs_errtbl[] = { + { NFS_OK, 0 }, + { NFSERR_PERM, EPERM }, + { NFSERR_NOENT, ENOENT }, + { NFSERR_IO, EIO }, + { NFSERR_NXIO, ENXIO }, + { NFSERR_ACCES, EACCES }, + { NFSERR_EXIST, EEXIST }, + { NFSERR_NODEV, ENODEV }, + { NFSERR_NOTDIR, ENOTDIR }, + { NFSERR_ISDIR, EISDIR }, + { NFSERR_INVAL, EINVAL }, + { NFSERR_FBIG, EFBIG }, + { NFSERR_NOSPC, ENOSPC }, + { NFSERR_ROFS, EROFS }, + { NFSERR_NAMETOOLONG, ENAMETOOLONG }, + { NFSERR_NOTEMPTY, ENOTEMPTY }, +#ifdef EDQUOT + { NFSERR_DQUOT, EDQUOT }, +#endif + { NFSERR_STALE, ESTALE }, + { NFSERR_WFLUSH, EIO }, + { -1, EIO } + }; + int i; + + for (i = 0; nfs_errtbl[i].nfserr != -1; i++) { + if (nfs_errtbl[i].syserr == errno) + return htonl (nfs_errtbl[i].nfserr); + } + printk (KERN_INFO "nfsd: non-standard errno: %d\n", errno); + return nfserr_io; +} + +#if 0 +static void +nfsd3_dump(char *tag, u32 *buf, int len) +{ + int i; + + printk(KERN_NOTICE + "nfsd: %s (%d words)\n", tag, len); + + for (i = 0; i < len && i < 32; i += 8) + printk(KERN_NOTICE + " %08lx %08lx %08lx %08lx" + " %08lx %08lx %08lx %08lx\n", + buf[i], buf[i+1], buf[i+2], buf[i+3], + buf[i+4], buf[i+5], buf[i+6], buf[i+7]); +} +#endif diff -u --recursive --new-file v2.1.31/linux/fs/nfsd/nfs3xdr.c linux/fs/nfsd/nfs3xdr.c --- v2.1.31/linux/fs/nfsd/nfs3xdr.c Wed Dec 31 16:00:00 1969 +++ linux/fs/nfsd/nfs3xdr.c Fri Apr 4 11:06:02 1997 @@ -0,0 +1,785 @@ +/* + * linux/fs/nfsd/nfs3xdr.c + * + * XDR support for nfsd/protocol version 3. + * + * Copyright (C) 1995, 1996 Olaf Kirch + */ + +#include +#include +#include + +#include +#include +#include +#include + +#define NFSDDBG_FACILITY NFSDDBG_XDR + +u32 nfs_ok, nfserr_perm, nfserr_noent, nfserr_io, nfserr_nxio, + nfserr_acces, nfserr_exist, nfserr_nodev, nfserr_notdir, + nfserr_isdir, nfserr_fbig, nfserr_nospc, nfserr_rofs, + nfserr_nametoolong, nfserr_dquot, nfserr_stale; + +#ifdef NFSD_OPTIMIZE_SPACE +# define inline +#endif + +/* + * Mapping of S_IF* types to NFS file types + */ +static u32 nfs3_ftypes[] = { + NF3NON, NF3FIFO, NF3CHR, NF3BAD, + NF3DIR, NF3BAD, NF3BLK, NF3BAD, + NF3REG, NF3BAD, NF3LNK, NF3BAD, + NF3SOCK, NF3BAD, NF3LNK, NF3BAD, +}; + +/* + * Initialization of NFS status variables + */ +void +nfs3xdr_init(void) +{ + static int inited = 0; + + if (inited) + return; + + nfs_ok = htonl(NFS_OK); + nfserr_perm = htonl(NFSERR_PERM); + nfserr_noent = htonl(NFSERR_NOENT); + nfserr_io = htonl(NFSERR_IO); + nfserr_nxio = htonl(NFSERR_NXIO); + nfserr_acces = htonl(NFSERR_ACCES); + nfserr_exist = htonl(NFSERR_EXIST); + nfserr_nodev = htonl(NFSERR_NODEV); + nfserr_notdir = htonl(NFSERR_NOTDIR); + nfserr_isdir = htonl(NFSERR_ISDIR); + nfserr_fbig = htonl(NFSERR_FBIG); + nfserr_nospc = htonl(NFSERR_NOSPC); + nfserr_rofs = htonl(NFSERR_ROFS); + nfserr_nametoolong = htonl(NFSERR_NAMETOOLONG); + nfserr_dquot = htonl(NFSERR_DQUOT); + nfserr_stale = htonl(NFSERR_STALE); + + inited = 1; +} + +/* + * XDR functions for basic NFS types + */ +static inline u32 * +enc64(u32 *p, u64 val) +{ + *p++ = (val >> 32); + *p++ = (val & 0xffffffff); + return p; +} + +static inline u32 * +dec64(u32 *p, u64 *valp) +{ + *valp = ((u64) ntohl(*p++)) << 32; + *valp |= ntohl(*p++); + return p; +} + +static inline u32 * +encode_time3(u32 *p, time_t secs) +{ + *p++ = htonl((u32) secs); *p++ = 0; + return p; +} + +static inline u32 * +decode_time3(u32 *p, time_t *secp) +{ + *secp = ntohl(*p++); + return p + 1; +} + +static inline u32 * +decode_fh(u32 *p, struct svc_fh *fhp) +{ + if (*p++ != sizeof(struct knfs_fh)) + return NULL; + + memcpy(&fhp->fh_handle, p, sizeof(struct knfs_fh)); + fhp->fh_inode = NULL; + fhp->fh_export = NULL; + + return p + (sizeof(struct knfs_fh) >> 2); +} + +static inline u32 * +encode_fh(u32 *p, struct svc_fh *fhp) +{ + *p++ = htonl(sizeof(struct knfs_fh)); + memcpy(p, &fhp->fh_handle, sizeof(struct knfs_fh)); + return p + (sizeof(struct knfs_fh) >> 2); +} + +/* + * Decode a file name and make sure that the path contains + * no slashes or null bytes. + */ +static inline u32 * +decode_filename(u32 *p, char **namp, int *lenp) +{ + char *name; + int i; + + if ((p = xdr_decode_string(p, namp, lenp, NFS3_MAXNAMLEN)) != NULL) { + for (i = 0, name = *namp; i < *lenp; i++, name++) { + if (*name == '\0' || *name == '/') + return NULL; + } + *name = '\0'; + } + + return p; +} + +static inline u32 * +decode_pathname(u32 *p, char **namp, int *lenp) +{ + char *name; + int i; + + if ((p = xdr_decode_string(p, namp, lenp, NFS3_MAXPATHLEN)) != NULL) { + for (i = 0, name = *namp; i < *lenp; i++, name++) { + if (*name == '\0') + return NULL; + } + *name = '\0'; + } + + return p; +} + +static inline u32 * +decode_sattr3(u32 *p, struct iattr *iap) +{ + u32 tmp; + + iap->ia_valid = 0; + + if (*p++) { + iap->ia_valid |= ATTR_MODE; + iap->ia_mode = ntohl(*p++); + } + if (*p++) { + iap->ia_valid |= ATTR_UID; + iap->ia_uid = ntohl(*p++); + } + if (*p++) { + iap->ia_valid |= ATTR_GID; + iap->ia_gid = ntohl(*p++); + } + if (*p++) { + iap->ia_valid |= ATTR_SIZE; + iap->ia_size = ntohl(*p++); + } + if ((tmp = *p++) == 1) { + iap->ia_valid |= ATTR_ATIME; + } else if (tmp == 2) { + iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET; + iap->ia_atime = ntohl(*p++), p++; + } + if ((tmp = *p++) != 0) { + iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET; + } else if (tmp == 2) { + iap->ia_valid |= ATTR_MTIME; + iap->ia_mtime = ntohl(*p++), p++; + } + return p; +} + +static inline u32 * +encode_fattr3(struct svc_rqst *rqstp, u32 *p, struct inode *inode) +{ + if (!inode) { + printk("nfsd: NULL inode in %s:%d", __FILE__, __LINE__); + return NULL; + } + + *p++ = htonl(nfs3_ftypes[(inode->i_mode & S_IFMT) >> 12]); + *p++ = htonl((u32) inode->i_mode); + *p++ = htonl((u32) inode->i_nlink); + *p++ = htonl((u32) nfsd_ruid(rqstp, inode->i_uid)); + *p++ = htonl((u32) nfsd_rgid(rqstp, inode->i_gid)); + if (S_ISLNK(inode->i_mode) && inode->i_size > NFS3_MAXPATHLEN) { + p = enc64(p, (u64) NFS3_MAXPATHLEN); + } else { + p = enc64(p, (u64) inode->i_size); + } + p = enc64(p, inode->i_blksize * inode->i_blocks); + *p++ = htonl((u32) MAJOR(inode->i_rdev)); + *p++ = htonl((u32) MINOR(inode->i_rdev)); + p = enc64(p, (u64) inode->i_dev); + p = enc64(p, (u64) inode->i_ino); + p = encode_time3(p, inode->i_atime); + p = encode_time3(p, inode->i_mtime); + p = encode_time3(p, inode->i_ctime); + + return p; +} + +/* + * Encode post-operation attributes. + * The inode may be NULL if the call failed because of a stale file + * handle. In this case, no attributes are returned. + */ +static u32 * +encode_post_op_attr(struct svc_rqst *rqstp, u32 *p, struct inode *inode) +{ + if (inode == NULL) { + *p++ = xdr_zero; + return p; + } + return encode_fattr3(rqstp, p, inode); +} + +/* + * Enocde weak cache consistency data + */ +static u32 * +encode_wcc_data(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp) +{ + struct inode *inode = fhp->fh_inode; + + if (fhp->fh_post_version == inode->i_version) { + *p++ = xdr_one; + p = enc64(p, (u64) fhp->fh_pre_size); + p = encode_time3(p, fhp->fh_pre_mtime); + p = encode_time3(p, fhp->fh_pre_ctime); + } else { + *p++ = xdr_zero; + } + return encode_post_op_attr(rqstp, p, inode); +} + +/* + * Check buffer bounds after decoding arguments + */ +static inline int +xdr_argsize_check(struct svc_rqst *rqstp, u32 *p) +{ + struct svc_buf *buf = &rqstp->rq_argbuf; + + return p - buf->base <= buf->buflen; +} + +static inline int +xdr_ressize_check(struct svc_rqst *rqstp, u32 *p) +{ + struct svc_buf *buf = &rqstp->rq_resbuf; + + buf->len = p - buf->base; + dprintk("nfsd: ressize_check p %p base %p len %d\n", + p, buf->base, buf->buflen); + return (buf->len <= buf->buflen); +} + +/* + * XDR decode functions + */ +int +nfs3svc_decode_fhandle(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp) +{ + if (!(p = decode_fh(p, fhp))) + return 0; + return xdr_argsize_check(rqstp, p); +} + +int +nfs3svc_decode_sattrargs(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_sattrargs *args) +{ + if (!(p = decode_fh(p, &args->fh)) + || !(p = decode_sattr3(p, &args->attrs)) + || (*p++ && !(p = decode_time3(p, &args->guardtime)))) + return 0; + + return xdr_argsize_check(rqstp, p); +} + +int +nfs3svc_decode_diropargs(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_diropargs *args) +{ + if (!(p = decode_fh(p, &args->fh)) + || !(p = decode_filename(p, &args->name, &args->len))) + return 0; + + return xdr_argsize_check(rqstp, p); +} + +int +nfs3svc_decode_accessargs(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_accessargs *args) +{ + if (!(p = decode_fh(p, &args->fh))) + return 0; + args->access = ntohl(*p++); + + return xdr_argsize_check(rqstp, p); +} + +int +nfs3svc_decode_readargs(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_readargs *args) +{ + if (!(p = decode_fh(p, &args->fh)) + || !(p = dec64(p, &args->offset)) + || !(p = dec64(p, &args->count))) + return 0; + + return xdr_argsize_check(rqstp, p); +} + +int +nfs3svc_decode_writeargs(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_writeargs *args) +{ + if (!(p = decode_fh(p, &args->fh)) + || !(p = dec64(p, &args->offset)) + || !(p = dec64(p, &args->count))) + return 0; + + args->stable = ntohl(*p++); + args->len = ntohl(*p++); + args->data = (char *) p; + p += (args->len + 3) >> 2; + + return xdr_argsize_check(rqstp, p); +} + +int +nfs3svc_decode_createargs(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_createargs *args) +{ + if (!(p = decode_fh(p, &args->fh)) + || !(p = decode_filename(p, &args->name, &args->len))) + return 0; + + switch (args->createmode = ntohl(*p++)) { + case 0: case 1: + if (!(p = decode_sattr3(p, &args->attrs))) + return 0; + break; + case 2: + args->verf = p; + p += 2; + break; + default: + return 0; + } + + return xdr_argsize_check(rqstp, p); +} +int +nfs3svc_decode_mkdirargs(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_createargs *args) +{ + if (!(p = decode_fh(p, &args->fh)) + || !(p = decode_filename(p, &args->name, &args->len)) + || !(p = decode_sattr3(p, &args->attrs))) + return 0; + + return xdr_argsize_check(rqstp, p); +} + +int +nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_symlinkargs *args) +{ + if (!(p = decode_fh(p, &args->ffh)) + || !(p = decode_filename(p, &args->fname, &args->flen)) + || !(p = decode_sattr3(p, &args->attrs)) + || !(p = decode_pathname(p, &args->tname, &args->tlen))) + return 0; + + return xdr_argsize_check(rqstp, p); +} + +int +nfs3svc_decode_mknodargs(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_mknodargs *args) +{ + if (!(p = decode_fh(p, &args->fh)) + || !(p = decode_filename(p, &args->name, &args->len))) + return 0; + + args->ftype = ntohl(*p++); + + if (args->ftype == NF3BLK || args->ftype == NF3CHR + || args->ftype == NF3SOCK || args->ftype == NF3FIFO) { + if (!(p = decode_sattr3(p, &args->attrs))) + return 0; + } + + if (args->ftype == NF3BLK || args->ftype == NF3CHR) { + args->major = ntohl(*p++); + args->minor = ntohl(*p++); + } + + return xdr_argsize_check(rqstp, p); +} + +int +nfs3svc_decode_renameargs(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_renameargs *args) +{ + if (!(p = decode_fh(p, &args->ffh)) + || !(p = decode_filename(p, &args->fname, &args->flen)) + || !(p = decode_fh(p, &args->tfh)) + || !(p = decode_filename(p, &args->tname, &args->tlen))) + return 0; + + return xdr_argsize_check(rqstp, p); +} + +int +nfs3svc_decode_linkargs(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_linkargs *args) +{ + if (!(p = decode_fh(p, &args->ffh)) + || !(p = decode_fh(p, &args->tfh)) + || !(p = decode_filename(p, &args->tname, &args->tlen))) + return 0; + + return xdr_argsize_check(rqstp, p); +} + +int +nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_readdirargs *args) +{ + if (!(p = decode_fh(p, &args->fh))) + return 0; + args->cookie = ntohl(*p++); + args->verf = p; p += 2; + args->count = ntohl(*p++); + + return xdr_argsize_check(rqstp, p); +} + +int +nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_readdirargs *args) +{ + if (!(p = decode_fh(p, &args->fh))) + return 0; + args->cookie = ntohl(*p++); + args->verf = p; p += 2; + args->dircount = ntohl(*p++); + args->count = ntohl(*p++); + + return xdr_argsize_check(rqstp, p); +} + +int +nfs3svc_decode_commitargs(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_commitargs *args) +{ + if (!(p = decode_fh(p, &args->fh)) + || !(p = dec64(p, &args->offset))) + return 0; + args->count = ntohl(*p++); + + return xdr_argsize_check(rqstp, p); +} + +/* + * XDR encode functions + */ +/* GETATTR */ +int +nfs3svc_encode_attrstat(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_attrstat *resp) +{ + if (!(p = encode_fattr3(rqstp, p, resp->fh.fh_inode))) + return 0; + return xdr_ressize_check(rqstp, p); +} + +/* SETATTR, REMOVE, RMDIR */ +int +nfs3svc_encode_wccstat(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_attrstat *resp) +{ + if (!(p = encode_wcc_data(rqstp, p, &resp->fh))) + return 0; + return xdr_ressize_check(rqstp, p); +} + +/* LOOKUP */ +int +nfs3svc_encode_lookupres(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_lookupres *resp) +{ + if (resp->status == 0) { + p = encode_fh(p, &resp->fh); + if (!(p = encode_fattr3(rqstp, p, resp->fh.fh_inode))) + return 0; + } + p = encode_post_op_attr(rqstp, p, resp->dirfh.fh_inode); + return xdr_ressize_check(rqstp, p); +} + +/* ACCESS */ +int +nfs3svc_encode_accessres(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_accessres *resp) +{ + p = encode_post_op_attr(rqstp, p, resp->fh.fh_inode); + if (resp->status == 0) + *p++ = htonl(resp->access); + return xdr_ressize_check(rqstp, p); +} + +/* READLINK */ +int +nfs3svc_encode_readlinkres(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_readlinkres *resp) +{ + p = encode_post_op_attr(rqstp, p, resp->fh.fh_inode); + if (resp->status == 0) { + *p++ = htonl(resp->len); + p += XDR_QUADLEN(resp->len); + } + return xdr_ressize_check(rqstp, p); +} + +/* READ */ +int +nfs3svc_encode_readres(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_readres *resp) +{ + p = encode_post_op_attr(rqstp, p, resp->fh.fh_inode); + if (resp->status == 0) { + *p++ = htonl(resp->count); + *p++ = htonl(resp->eof); + *p++ = htonl(resp->count); /* xdr opaque count */ + p += XDR_QUADLEN(resp->count); + } + return xdr_ressize_check(rqstp, p); +} + +/* WRITE */ +int +nfs3svc_encode_writeres(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_writeres *resp) +{ + p = encode_wcc_data(rqstp, p, &resp->fh); + if (resp->status == 0) { + *p++ = htonl(resp->count); + *p++ = htonl(resp->committed); + *p++ = htonl(nfssvc_boot.tv_sec); + *p++ = htonl(nfssvc_boot.tv_usec); + } + return xdr_ressize_check(rqstp, p); +} + +/* CREATE, MKDIR, SYMLINK, MKNOD */ +int +nfs3svc_encode_createres(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_createres *resp) +{ + if (resp->status == 0) { + p = encode_fh(p, &resp->fh); + p = encode_post_op_attr(rqstp, p, resp->fh.fh_inode); + } + p = encode_wcc_data(rqstp, p, &resp->dirfh); + return xdr_ressize_check(rqstp, p); +} + +/* RENAME */ +int +nfs3svc_encode_renameres(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_renameres *resp) +{ + p = encode_wcc_data(rqstp, p, &resp->ffh); + p = encode_wcc_data(rqstp, p, &resp->tfh); + return xdr_ressize_check(rqstp, p); +} + +/* LINK */ +int +nfs3svc_encode_linkres(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_linkres *resp) +{ + p = encode_post_op_attr(rqstp, p, resp->fh.fh_inode); + p = encode_wcc_data(rqstp, p, &resp->tfh); + return xdr_ressize_check(rqstp, p); +} + +/* READDIR */ +int +nfs3svc_encode_readdirres(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_readdirres *resp) +{ + p = encode_post_op_attr(rqstp, p, resp->fh.fh_inode); + if (resp->status == 0) { + /* stupid readdir cookie */ + *p++ = ntohl(resp->fh.fh_inode->i_mtime); + *p++ = xdr_zero; + p = resp->list_end; + } + + return xdr_ressize_check(rqstp, p); +} + +#define NFS3_ENTRYPLUS_BAGGAGE ((1 + 20 + 1 + NFS3_FHSIZE) << 2) +int +nfs3svc_encode_entry(struct readdir_cd *cd, const char *name, + int namlen, unsigned long offset, ino_t ino) +{ + u32 *p = cd->buffer; + int buflen, slen, elen; + struct svc_fh fh; + + if (offset > ~((u64) 0)) + return -EINVAL; + if (cd->offset) + *cd->offset = htonl(offset); + + /* For readdirplus, look up the inode */ + if (cd->plus && nfsd_lookup(cd->rqstp, cd->dirfh, name, namlen, &fh)) + return 0; + + /* truncate filename if too long */ + if (namlen > NFS3_MAXNAMLEN) + namlen = NFS3_MAXNAMLEN; + + slen = XDR_QUADLEN(namlen); + elen = slen + (cd->plus? NFS3_ENTRYPLUS_BAGGAGE : 0); + if ((buflen = cd->buflen - elen - 4) < 0) { + cd->eob = 1; + if (cd->plus) + fh_put(&fh); + return -EINVAL; + } + *p++ = xdr_one; /* mark entry present */ + *p++ = xdr_zero; /* file id (64 bit) */ + *p++ = htonl((u32) ino); + *p++ = htonl((u32) namlen); /* name length & name */ + memcpy(p, name, namlen); + p += slen; + + /* throw in readdirplus baggage */ + if (cd->plus) { + p = encode_post_op_attr(cd->rqstp, p, fh.fh_inode); + p = encode_fh(p, &fh); + fh_put(&fh); + } + + cd->offset = p; /* remember pointer */ + p = enc64(p, ~(u64) 0); /* offset of next entry */ + + cd->buflen = buflen; + cd->buffer = p; + return 0; +} + +/* FSSTAT */ +int +nfs3svc_encode_statfsres(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_statfsres *resp) +{ + struct statfs *s = &resp->stats; + u64 bs = s->f_bsize; + + *p++ = xdr_zero; /* no post_op_attr */ + + if (resp->status == 0) { + p = enc64(p, bs * s->f_blocks); /* total bytes */ + p = enc64(p, bs * s->f_bfree); /* free bytes */ + p = enc64(p, bs * s->f_bavail); /* user available bytes */ + p = enc64(p, s->f_files); /* total inodes */ + p = enc64(p, s->f_ffree); /* free inodes */ + p = enc64(p, s->f_ffree); /* user available inodes */ + *p++ = htonl(resp->invarsec); /* mean unchanged time */ + } + return xdr_ressize_check(rqstp, p); +} + +/* FSINFO */ +int +nfs3svc_encode_fsinfores(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_fsinfores *resp) +{ + *p++ = xdr_zero; /* no post_op_attr */ + + if (resp->status == 0) { + *p++ = htonl(resp->f_rtmax); + *p++ = htonl(resp->f_rtpref); + *p++ = htonl(resp->f_rtmult); + *p++ = htonl(resp->f_wtmax); + *p++ = htonl(resp->f_wtpref); + *p++ = htonl(resp->f_wtmult); + *p++ = htonl(resp->f_dtpref); + *p++ = htonl(resp->f_maxfilesize); + *p++ = xdr_zero; + *p++ = htonl(1000000000 / HZ); + *p++ = htonl(resp->f_properties); + } + + return xdr_ressize_check(rqstp, p); +} + +/* PATHCONF */ +int +nfs3svc_encode_pathconfres(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_pathconfres *resp) +{ + *p++ = xdr_zero; /* no post_op_attr */ + + if (resp->status == 0) { + *p++ = htonl(resp->p_link_max); + *p++ = htonl(resp->p_name_max); + *p++ = xdr_one; /* always reject long file names */ + *p++ = xdr_one; /* chown restricted */ + *p++ = htonl(resp->p_case_insensitive); + *p++ = htonl(resp->p_case_preserving); + } + + return xdr_ressize_check(rqstp, p); +} + +/* COMMIT */ +int +nfs3svc_encode_commitres(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_commitres *resp) +{ + p = encode_wcc_data(rqstp, p, &resp->fh); + /* Write verifier */ + if (resp->status == 0) { + *p++ = htonl(nfssvc_boot.tv_sec); + *p++ = htonl(nfssvc_boot.tv_usec); + } + return xdr_ressize_check(rqstp, p); +} + +/* + * XDR release functions + */ +int +nfs3svc_release_fhandle(struct svc_rqst *rqstp, u32 *p, + struct nfsd_fhandle *resp) +{ + fh_put(&resp->fh); + return 1; +} + +int +nfs3svc_release_fhandle2(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_fhandle2 *resp) +{ + fh_put(&resp->fh1); + fh_put(&resp->fh2); + return 1; +} diff -u --recursive --new-file v2.1.31/linux/fs/nfsd/nfscache.c linux/fs/nfsd/nfscache.c --- v2.1.31/linux/fs/nfsd/nfscache.c Wed Dec 31 16:00:00 1969 +++ linux/fs/nfsd/nfscache.c Fri Apr 4 11:06:02 1997 @@ -0,0 +1,321 @@ +/* + * linux/fs/nfsd/nfscache.c + * + * Request reply cache. This is currently a global cache, but this may + * change in the future and be a per-client cache. + * + * This code is heavily inspired by the 44BSD implementation, although + * it does things a bit differently. + * + * Copyright (C) 1995, 1996 Olaf Kirch + */ + +#include +#include +#include +#include + +#include +#include +#include + +/* Size of reply cache. Common values are: + * 4.3BSD: 128 + * 4.4BSD: 256 + * Solaris2: 1024 + * DEC Unix: 512-4096 + */ +#define CACHESIZE 1024 +#define HASHSIZE 64 +#define REQHASH(xid) ((((xid) >> 24) ^ (xid)) & (HASHSIZE-1)) + +struct nfscache_head { + struct svc_cacherep * next; + struct svc_cacherep * prev; +}; + +static struct nfscache_head hash_list[HASHSIZE]; +static struct svc_cacherep * lru_head; +static struct svc_cacherep * lru_tail; +static struct svc_cacherep nfscache[CACHESIZE]; +static int cache_initialized = 0; +static int cache_disabled = 1; + +static int nfsd_cache_append(struct svc_rqst *rqstp, struct svc_buf *data); + +void +nfsd_cache_init(void) +{ + struct svc_cacherep *rp; + struct nfscache_head *rh; + int i; + + if (cache_initialized) + return; + + for (i = 0, rh = hash_list; i < HASHSIZE; i++, rh++) + rh->next = rh->prev = (struct svc_cacherep *) rh; + + for (i = 0, rp = nfscache; i < CACHESIZE; i++, rp++) { + rp->c_state = RC_UNUSED; + rp->c_type = RC_NOCACHE; + rp->c_hash_next = + rp->c_hash_prev = rp; + rp->c_lru_next = rp + 1; + rp->c_lru_prev = rp - 1; + } + lru_head = nfscache; + lru_tail = nfscache + CACHESIZE - 1; + lru_head->c_lru_prev = NULL; + lru_tail->c_lru_next = NULL; + + cache_initialized = 1; + cache_disabled = 0; +} + +void +nfsd_cache_shutdown(void) +{ + struct svc_cacherep *rp; + + if (!cache_initialized) + return; + + for (rp = lru_head; rp; rp = rp->c_lru_next) { + if (rp->c_state == RC_DONE && rp->c_type == RC_REPLBUFF) + kfree(rp->c_replbuf.buf); + } + + cache_initialized = 0; + cache_disabled = 1; +} + +/* + * Move cache entry to front of LRU list + */ +static void +lru_put_front(struct svc_cacherep *rp) +{ + struct svc_cacherep *prev = rp->c_lru_prev, + *next = rp->c_lru_next; + + if (prev) + prev->c_lru_next = next; + else + lru_head = next; + if (next) + next->c_lru_prev = prev; + else + lru_tail = prev; + + rp->c_lru_next = lru_head; + rp->c_lru_prev = NULL; + if (lru_head) + lru_head->c_lru_prev = rp; + lru_head = rp; +} + +/* + * Move a cache entry from one hash list to another + */ +static void +hash_refile(struct svc_cacherep *rp) +{ + struct svc_cacherep *prev = rp->c_hash_prev, + *next = rp->c_hash_next; + struct nfscache_head *head = hash_list + REQHASH(rp->c_xid); + + prev->c_hash_next = next; + next->c_hash_prev = prev; + + rp->c_hash_next = head->next; + rp->c_hash_prev = (struct svc_cacherep *) head; + head->next->c_hash_prev = rp; + head->next = rp; +} + +/* + * Try to find an entry matching the current call in the cache. When none + * is found, we grab the oldest unlocked entry off the LRU list. + * Note that no operation within the loop may sleep. + */ +int +nfsd_cache_lookup(struct svc_rqst *rqstp, int type) +{ + struct svc_cacherep *rh, *rp; + struct svc_client *clp = rqstp->rq_client; + u32 xid = rqstp->rq_xid, + proc = rqstp->rq_proc; + unsigned long age; + + rqstp->rq_cacherep = NULL; + if (cache_disabled || type == RC_NOCACHE) { + nfsdstats.rcnocache++; + return RC_DOIT; + } + + rp = rh = (struct svc_cacherep *) &hash_list[REQHASH(xid)]; + while ((rp = rp->c_hash_next) != rh) { + if (rp->c_state != RC_UNUSED && + xid == rp->c_xid && proc == rp->c_proc && + exp_checkaddr(clp, rp->c_client)) { + nfsdstats.rchits++; + goto found_entry; + } + } + nfsdstats.rcmisses++; + + /* This loop shouldn't take more than a few iterations normally */ + { + int safe = 0; + for (rp = lru_tail; rp; rp = rp->c_lru_prev) { + if (rp->c_state != RC_INPROG) + break; + if (safe++ > CACHESIZE) { + printk("nfsd: loop in repcache LRU list\n"); + cache_disabled = 1; + return RC_DOIT; + } + } + } + + /* This should not happen */ + if (rp == NULL) { + static int complaints = 0; + + printk(KERN_WARNING "nfsd: all repcache entries locked!\n"); + if (++complaints > 5) { + printk(KERN_WARNING "nfsd: disabling repcache.\n"); + cache_disabled = 1; + } + return RC_DOIT; + } + + rqstp->rq_cacherep = rp; + rp->c_state = RC_INPROG; + rp->c_xid = xid; + rp->c_proc = proc; + rp->c_client = rqstp->rq_addr.sin_addr; + hash_refile(rp); + + /* release any buffer */ + if (rp->c_type == RC_REPLBUFF) { + kfree(rp->c_replbuf.buf); + rp->c_replbuf.buf = NULL; + } + rp->c_type = RC_NOCACHE; + + return RC_DOIT; + +found_entry: + /* We found a matching entry which is either in progress or done. */ + age = jiffies - rp->c_timestamp; + rp->c_timestamp = jiffies; + lru_put_front(rp); + + /* Request being processed or excessive rexmits */ + if (rp->c_state == RC_INPROG || age < RC_DELAY) + return RC_DROPIT; + + /* From the hall of fame of impractical attacks: + * Is this a user who tries to snoop on the cache? */ + if (!rqstp->rq_secure && rp->c_secure) + return RC_DOIT; + + /* Compose RPC reply header */ + switch (rp->c_type) { + case RC_NOCACHE: + return RC_DOIT; + case RC_REPLSTAT: + svc_putlong(&rqstp->rq_resbuf, rp->c_replstat); + break; + case RC_REPLBUFF: + if (!nfsd_cache_append(rqstp, &rp->c_replbuf)) + return RC_DOIT; /* should not happen */ + break; + default: + printk(KERN_WARNING "nfsd: bad repcache type %d\n", rp->c_type); + rp->c_state = RC_UNUSED; + return RC_DOIT; + } + + return RC_REPLY; +} + +/* + * Update a cache entry. This is called from nfsd_dispatch when + * the procedure has been executed and the complete reply is in + * rqstp->rq_res. + * + * We're copying around data here rather than swapping buffers because + * the toplevel loop requires max-sized buffers, which would be a waste + * of memory for a cache with a max reply size of 100 bytes (diropokres). + * + * If we should start to use different types of cache entries tailored + * specifically for attrstat and fh's, we may save even more space. + * + * Also note that a cachetype of RC_NOCACHE can legally be passed when + * nfsd failed to encode a reply that otherwise would have been cached. + * In this case, nfsd_cache_update is called with statp == NULL. + */ +void +nfsd_cache_update(struct svc_rqst *rqstp, int cachetype, u32 *statp) +{ + struct svc_cacherep *rp; + struct svc_buf *resp = &rqstp->rq_resbuf, *cachp; + int len; + + if (!(rp = rqstp->rq_cacherep) || cache_disabled) + return; + + /* Don't cache excessive amounts of data and XDR failures */ + if (!statp || (len = resp->buf - statp) > (256 >> 2)) { + rp->c_state = RC_UNUSED; + return; + } + + switch (cachetype) { + case RC_REPLSTAT: + if (len != 1) + printk("nfsd: RC_REPLSTAT/reply len %d!\n",len); + rp->c_replstat = *statp; + break; + case RC_REPLBUFF: + cachp = &rp->c_replbuf; + cachp->buf = (u32 *) kmalloc(len << 2, GFP_KERNEL); + if (!cachp->buf) { + rp->c_state = RC_UNUSED; + return; + } + cachp->len = len; + memcpy(cachp->buf, statp, len << 2); + break; + } + + lru_put_front(rp); + rp->c_secure = rqstp->rq_secure; + rp->c_type = cachetype; + rp->c_state = RC_DONE; + rp->c_timestamp = jiffies; + + return; +} + +/* + * Copy cached reply to current reply buffer. Should always fit. + */ +static int +nfsd_cache_append(struct svc_rqst *rqstp, struct svc_buf *data) +{ + struct svc_buf *resp = &rqstp->rq_resbuf; + + if (resp->len + data->len > resp->buflen) { + printk(KERN_WARNING "nfsd: cached reply too large (%d).\n", + data->len); + return 0; + } + memcpy(resp->buf, data->buf, data->len); + resp->buf += ((data->len + 3) >> 2); + resp->len += data->len; + return 1; +} diff -u --recursive --new-file v2.1.31/linux/fs/nfsd/nfsctl.c linux/fs/nfsd/nfsctl.c --- v2.1.31/linux/fs/nfsd/nfsctl.c Wed Dec 31 16:00:00 1969 +++ linux/fs/nfsd/nfsctl.c Fri Apr 4 11:06:02 1997 @@ -0,0 +1,238 @@ +/* + * linux/fs/nfsd/nfsctl.c + * + * Syscall interface to knfsd. + * + * Copyright (C) 1995, 1996 Olaf Kirch + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#if LINUX_VERSION_CODE >= 0x020100 +#include +#else +# define copy_from_user memcpy_fromfs +# define copy_to_user memcpy_tofs +# define access_ok !verify_area +#endif + +extern long sys_call_table[]; + +static int nfsctl_svc(struct nfsctl_svc *data); +static int nfsctl_addclient(struct nfsctl_client *data); +static int nfsctl_delclient(struct nfsctl_client *data); +static int nfsctl_export(struct nfsctl_export *data); +static int nfsctl_unexport(struct nfsctl_export *data); +static int nfsctl_getfh(struct nfsctl_fhparm *, struct knfs_fh *); +/* static int nfsctl_ugidupdate(struct nfsctl_ugidmap *data); */ + +static int initialized = 0; + +/* + * Initialize nfsd + */ +static void +nfsd_init(void) +{ + nfsd_xdr_init(); /* XDR */ +#ifdef CONFIG_PROC_FS + nfsd_stat_init(); /* Statistics */ +#endif + nfsd_cache_init(); /* RPC reply cache */ + nfsd_export_init(); /* Exports table */ + nfsd_lockd_init(); /* lockd->nfsd callbacks */ + nfsd_racache_init(); /* Readahead param cache */ + initialized = 1; +} + +static inline int +nfsctl_svc(struct nfsctl_svc *data) +{ + return nfsd_svc(data->svc_port, data->svc_nthreads); +} + +static inline int +nfsctl_addclient(struct nfsctl_client *data) +{ + return exp_addclient(data); +} + +static inline int +nfsctl_delclient(struct nfsctl_client *data) +{ + return exp_delclient(data); +} + +static inline int +nfsctl_export(struct nfsctl_export *data) +{ + return exp_export(data); +} + +static inline int +nfsctl_unexport(struct nfsctl_export *data) +{ + return exp_unexport(data); +} + +#ifdef notyet +static inline int +nfsctl_ugidupdate(nfs_ugidmap *data) +{ + return -EINVAL; +} +#endif + +static inline int +nfsctl_getfh(struct nfsctl_fhparm *data, struct knfs_fh *res) +{ + struct sockaddr_in *sin; + struct svc_client *clp; + int err = 0; + + if (data->gf_addr.sa_family != AF_INET) + return -EPROTONOSUPPORT; + if (data->gf_version < 2 || data->gf_version > NFSSVC_MAXVERS) + return -EINVAL; + sin = (struct sockaddr_in *)&data->gf_addr; + + exp_readlock(); + if (!(clp = exp_getclient(sin))) + err = -EPERM; + else + err = exp_rootfh(clp, data->gf_dev, data->gf_ino, res); + exp_unlock(); + + return err; +} + +int +asmlinkage sys_nfsservctl(int cmd, struct nfsctl_arg *argp, union nfsctl_res *resp) +{ + struct nfsctl_arg * arg = NULL; + union nfsctl_res * res = NULL; + int err; + + if (!initialized) + nfsd_init(); + if (!suser()) + return -EPERM; + if (!access_ok(VERIFY_READ, argp, sizeof(*argp)) + || (resp && !access_ok(VERIFY_WRITE, resp, sizeof(*resp)))) + return -EFAULT; + if (!(arg = kmalloc(sizeof(*arg), GFP_USER)) || + (resp && !(res = kmalloc(sizeof(*res), GFP_USER)))) { + err = -ENOMEM; /* ??? */ + goto done; + } + copy_from_user(arg, argp, sizeof(*argp)); + if (arg->ca_version != NFSCTL_VERSION) { + printk(KERN_WARNING "nfsd: incompatible version in syscall.\n"); + err = -EINVAL; + goto done; + } + + MOD_INC_USE_COUNT; + switch(cmd) { + case NFSCTL_SVC: + err = nfsctl_svc(&arg->ca_svc); + break; + case NFSCTL_ADDCLIENT: + err = nfsctl_addclient(&arg->ca_client); + break; + case NFSCTL_DELCLIENT: + err = nfsctl_delclient(&arg->ca_client); + break; + case NFSCTL_EXPORT: + err = nfsctl_export(&arg->ca_export); + break; + case NFSCTL_UNEXPORT: + err = nfsctl_unexport(&arg->ca_export); + break; +#ifdef notyet + case NFSCTL_UGIDUPDATE: + err = nfsctl_ugidupdate(&arg->ca_umap); + break; +#endif + case NFSCTL_GETFH: + err = nfsctl_getfh(&arg->ca_getfh, &res->cr_getfh); + break; + default: + err = -EINVAL; + } + MOD_DEC_USE_COUNT; + + if (!err && resp) + copy_to_user(resp, res, sizeof(*resp)); + +done: + if (arg) + kfree(arg); + if (res) + kfree(res); + + return err; +} + +#ifdef MODULE +/* New-style module support since 2.1.18 */ +#if LINUX_VERSION_CODE >= 131346 +EXPORT_NO_SYMBOLS; +MODULE_AUTHOR("Olaf Kirch "); +#endif + +static unsigned long old_syscallvec; + +/* + * Initialize the module + */ +int +init_module(void) +{ + printk("Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n"); + + old_syscallvec = sys_call_table[__NR_nfsservctl]; + sys_call_table[__NR_nfsservctl] = (unsigned long) sys_nfsservctl; + nfsd_init(); + return 0; +} + +/* + * Clean up the mess before unloading the module + */ +void +cleanup_module(void) +{ + if (MOD_IN_USE) { + printk("nfsd: nfsd busy, remove delayed\n"); + return; + } + sys_call_table[__NR_nfsservctl] = old_syscallvec; + nfsd_export_shutdown(); + nfsd_cache_shutdown(); +#ifdef CONFIG_PROC_FS + nfsd_stat_shutdown(); +#endif + nfsd_lockd_shutdown(); +} +#endif diff -u --recursive --new-file v2.1.31/linux/fs/nfsd/nfsfh.c linux/fs/nfsd/nfsfh.c --- v2.1.31/linux/fs/nfsd/nfsfh.c Wed Dec 31 16:00:00 1969 +++ linux/fs/nfsd/nfsfh.c Fri Apr 4 11:06:02 1997 @@ -0,0 +1,124 @@ +/* + * linux/fs/nfsd/nfsfh.c + * + * NFS server filehandle treatment. + * + * Copyright (C) 1995, 1996 Olaf Kirch + */ + +#include +#include +#include +#include +#include + +#include +#include + +#define NFSDDBG_FACILITY NFSDDBG_FH + +/* + * Get the inode version number + */ +static inline int +nfsd_iversion(struct inode *inode) +{ + if (inode->i_sb->s_magic == EXT2_SUPER_MAGIC) + return inode->u.ext2_i.i_version; + return 0; +} + +/* + * Get the inode given a file handle. + */ +u32 +fh_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) +{ + struct svc_export *exp; + struct inode *inode; + struct knfs_fh *fh = &fhp->fh_handle; + + /* Already checked */ + if (fhp->fh_inode) + return 0; + + dprintk("nfsd: fh_lookup(exp %x/%ld fh %x/%ld)\n", + fh->fh_xdev, fh->fh_xino, fh->fh_dev, fh->fh_ino); + + /* Make sure that clients don't cheat */ + if (fh->fh_dev != fh->fh_xdev) { + printk(KERN_NOTICE "nfsd: fh with bad dev fields " + "(%x != %x) from %08lx:%d\n", + fh->fh_dev, fh->fh_xdev, + ntohl(rqstp->rq_addr.sin_addr.s_addr), + ntohs(rqstp->rq_addr.sin_port)); + return nfserr_perm; + } + + /* Look up the export entry */ + exp = exp_get(rqstp->rq_client, fh->fh_xdev, fh->fh_xino); + if (!exp) { + /* nfsdstats.fhstale++; */ + return nfserr_stale; /* export entry revoked */ + } + + /* Check if the request originated from a secure port. */ + if (!rqstp->rq_secure && EX_SECURE(exp)) { + printk(KERN_WARNING + "nfsd: request from insecure port (%08lx:%d)!\n", + ntohl(rqstp->rq_addr.sin_addr.s_addr), + ntohs(rqstp->rq_addr.sin_port)); + return nfserr_perm; + } + + /* Set user creds if we haven't done so already */ + nfsd_setuser(rqstp, exp); + + /* Get the inode */ + if (!(inode = nfsd_iget(fh->fh_dev, fh->fh_ino)) + || !inode->i_nlink || fh->fh_version != nfsd_iversion(inode)) { + if (inode) + iput(inode); + /* nfsdstats.fhstale++; */ + return nfserr_stale; /* unlinked in the meanwhile */ + } + + /* This is basically what wait_on_inode does */ + while (inode->i_lock) + sleep_on(&inode->i_wait); + fhp->fh_inode = inode; + fhp->fh_export = exp; + + /* Type check. The correct error return for type mismatches + * does not seem to be generally agreed upon. SunOS seems to + * use EISDIR if file isn't S_IFREG; a comment in the NFSv3 + * spec says this is incorrect (implementation notes for the + * write call). + */ + if (type > 0 && (inode->i_mode & S_IFMT) != type) + return (type == S_IFDIR)? nfserr_notdir : nfserr_isdir; + if (type < 0 && (inode->i_mode & S_IFMT) == -type) + return (type == -S_IFDIR)? nfserr_notdir : nfserr_isdir; + + /* Finally, check access permissions */ + return nfsd_permission(fhp->fh_export, inode, access); +} + +/* + * Compose file handle for NFS reply. + */ +void +fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct inode *inode) +{ + dprintk("nfsd: fh_compose(exp %x/%ld fh %x/%ld)\n", + exp->ex_dev, exp->ex_ino, inode->i_dev, inode->i_ino); + + fh_init(fhp); /* initialize empty fh */ + fhp->fh_inode = inode; + fhp->fh_export = exp; + fhp->fh_handle.fh_dev = inode->i_dev; + fhp->fh_handle.fh_ino = inode->i_ino; + fhp->fh_handle.fh_xdev = exp->ex_dev; + fhp->fh_handle.fh_xino = exp->ex_ino; + fhp->fh_handle.fh_version = nfsd_iversion(inode); +} diff -u --recursive --new-file v2.1.31/linux/fs/nfsd/nfsproc.c linux/fs/nfsd/nfsproc.c --- v2.1.31/linux/fs/nfsd/nfsproc.c Wed Dec 31 16:00:00 1969 +++ linux/fs/nfsd/nfsproc.c Fri Apr 4 11:06:02 1997 @@ -0,0 +1,581 @@ +/* + * nfsproc2.c Process version 2 NFS requests. + * + * Copyright (C) 1995 Olaf Kirch + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +typedef struct svc_rqst svc_rqst; +typedef struct svc_buf svc_buf; + +#define NFSDDBG_FACILITY NFSDDBG_PROC + +#define sleep(msec) \ + { printk(KERN_NOTICE "nfsd: sleeping %d msecs\n", msec); \ + current->state = TASK_INTERRUPTIBLE; \ + current->timeout = jiffies + msec / 10; \ + schedule(); \ + } +#define RETURN(st) return st + +static void +svcbuf_reserve(struct svc_buf *buf, u32 **ptr, int *len, int nr) +{ + *ptr = buf->buf + nr; + *len = buf->buflen - buf->len - nr; +} + +static int +nfsd_proc_null(struct svc_rqst *rqstp, void *argp, void *resp) +{ + RETURN(nfs_ok); +} + +/* + * Get a file's attributes + */ +static int +nfsd_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle *argp, + struct nfsd_attrstat *resp) +{ + dprintk("nfsd: GETATTR %x/%ld\n", + SVCFH_DEV(&argp->fh), + SVCFH_INO(&argp->fh)); + + fh_copy(&resp->fh, &argp->fh); + RETURN(fh_lookup(rqstp, &resp->fh, 0, MAY_NOP)); +} + +/* + * Set a file's attributes + */ +static int +nfsd_proc_setattr(struct svc_rqst *rqstp, struct nfsd_sattrargs *argp, + struct nfsd_attrstat *resp) +{ + dprintk("nfsd: SETATTR %x/%ld\n", + SVCFH_DEV(&argp->fh), + SVCFH_INO(&argp->fh)); + + fh_copy(&resp->fh, &argp->fh); + RETURN(nfsd_setattr(rqstp, &resp->fh, &argp->attrs)); +} + +/* + * Look up a path name component + */ +static int +nfsd_proc_lookup(struct svc_rqst *rqstp, struct nfsd_diropargs *argp, + struct nfsd_diropres *resp) +{ + int nfserr; + + dprintk("nfsd: LOOKUP %x/%ld %s\n", + SVCFH_DEV(&argp->fh), + SVCFH_INO(&argp->fh), + argp->name); + + nfserr = nfsd_lookup(rqstp, &argp->fh, + argp->name, + argp->len, + &resp->fh); + + fh_put(&argp->fh); + RETURN(nfserr); +} + +/* + * Read a symlink. + */ +static int +nfsd_proc_readlink(struct svc_rqst *rqstp, struct nfsd_fhandle *argp, + struct nfsd_readlinkres *resp) +{ + u32 *path; + int dummy, nfserr; + + dprintk("nfsd: READLINK %x/%ld\n", + SVCFH_DEV(&argp->fh), + SVCFH_INO(&argp->fh)); + + /* Reserve room for status and path length */ + svcbuf_reserve(&rqstp->rq_resbuf, &path, &dummy, 2); + + /* Read the symlink. */ + resp->len = NFS_MAXPATHLEN; + nfserr = nfsd_readlink(rqstp, &argp->fh, (char *) path, &resp->len); + + fh_put(&argp->fh); + RETURN(nfserr); +} + +/* + * Read a portion of a file. + */ +static int +nfsd_proc_read(struct svc_rqst *rqstp, struct nfsd_readargs *argp, + struct nfsd_readres *resp) +{ + u32 * buffer; + int nfserr, avail; + + dprintk("nfsd: READ %x/%ld %d bytes at %d\n", + SVCFH_DEV(&argp->fh), + SVCFH_INO(&argp->fh), + argp->count, argp->offset); + + /* Obtain buffer pointer for payload. 19 is 1 word for + * status, 17 words for fattr, and 1 word for the byte count. + */ + svcbuf_reserve(&rqstp->rq_resbuf, &buffer, &avail, 19); + + if ((avail << 2) < argp->count) { + printk(KERN_NOTICE + "oversized read request from %08lx:%d (%d bytes)\n", + ntohl(rqstp->rq_addr.sin_addr.s_addr), + ntohs(rqstp->rq_addr.sin_port), + argp->count); + argp->count = avail; + } + + resp->count = argp->count; + nfserr = nfsd_read(rqstp, fh_copy(&resp->fh, &argp->fh), + argp->offset, + (char *) buffer, + &resp->count); + + RETURN(nfserr); +} + +/* + * Write data to a file + */ +static int +nfsd_proc_write(struct svc_rqst *rqstp, struct nfsd_writeargs *argp, + struct nfsd_attrstat *resp) +{ + int nfserr; + + dprintk("nfsd: WRITE %x/%ld %d bytes at %d\n", + SVCFH_DEV(&argp->fh), + SVCFH_INO(&argp->fh), + argp->len, argp->offset); + + nfserr = nfsd_write(rqstp, fh_copy(&resp->fh, &argp->fh), + argp->offset, + argp->data, + argp->len, + 0); + RETURN(nfserr); +} + +/* + * CREATE processing is complicated. The keyword here is `overloaded.' + * There's a small race condition here between the check for existence + * and the actual create() call, but one could even consider this a + * feature because this only happens if someone else creates the file + * at the same time. + */ +static int +nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp, + struct nfsd_diropres *resp) +{ + struct inode *dirp, *inode = NULL; + struct iattr *attr; + svc_fh *dirfhp, *newfhp = NULL; + int nfserr, type, mode; + int rdonly = 0, exists; + dev_t rdev = NODEV; + + dprintk("nfsd: CREATE %x/%ld %s\n", + SVCFH_DEV(&argp->fh), + SVCFH_INO(&argp->fh), + argp->name); + + dirfhp = &argp->fh; + newfhp = &resp->fh; + attr = &argp->attrs; + + /* Get the directory inode */ + nfserr = fh_lookup(rqstp, dirfhp, S_IFDIR, MAY_EXEC); + if (nfserr) + RETURN(nfserr); + dirp = dirfhp->fh_inode; + + /* Check for MAY_WRITE separately. */ + nfserr = nfsd_permission(dirfhp->fh_export, dirp, MAY_WRITE); + if (nfserr == nfserr_rofs) { + rdonly = 1; /* Non-fatal error for echo > /dev/null */ + } else if (nfserr) { + fh_put(dirfhp); + RETURN(nfserr); + } + + /* First, check if the file already exists. */ + exists = !nfsd_lookup(rqstp, dirfhp, argp->name, argp->len, newfhp); + inode = newfhp->fh_inode; + + /* Unfudge the mode bits */ + if (attr->ia_valid & ATTR_MODE) { + type = attr->ia_mode & S_IFMT; + mode = attr->ia_mode & ~S_IFMT; + if (!type) /* HP weirdness */ + type = S_IFREG; + } else if (exists) { + type = inode->i_mode & S_IFMT; + mode = inode->i_mode & ~S_IFMT; + } else { + type = S_IFREG; + mode = 0; /* ??? */ + } + + /* This is for "echo > /dev/null" a la SunOS. Argh. */ + if (rdonly && (!exists || type == S_IFREG)) { + nfserr = nfserr_rofs; + goto done; + } + + attr->ia_valid |= ATTR_MODE; + attr->ia_mode = type | mode; + + /* Special treatment for non-regular files according to the + * gospel of sun micro + */ + nfserr = 0; + if (type != S_IFREG) { + int is_borc = 0; + u32 size = attr->ia_size; + + rdev = (dev_t) size; + if (type != S_IFBLK && type != S_IFCHR) { + rdev = 0; + } else if (type == S_IFCHR && size == ~(u32) 0) { + /* If you think you've seen the worst, grok this. */ + attr->ia_mode = S_IFIFO | mode; + type = S_IFIFO; + } else if (size != rdev) { + /* dev got truncated because of 16bit Linux dev_t */ + nfserr = nfserr_io; /* or nfserr_inval? */ + goto done; + } else { + /* Okay, char or block special */ + is_borc = 1; + } + + /* Make sure the type and device matches */ + if (exists && (type != (inode->i_mode & S_IFMT) + || (is_borc && inode->i_rdev != rdev))) { + nfserr = nfserr_exist; + goto done; + } + } + + if (!exists) { + /* File doesn't exist. Create it and set attrs */ + nfserr = nfsd_create(rqstp, dirfhp, argp->name, argp->len, + attr, type, rdev, newfhp); + } else if (type == S_IFREG) { + /* File already exists. We ignore all attributes except + * size, so that creat() behaves exactly like + * open(..., O_CREAT|O_TRUNC|O_WRONLY). + */ + if ((attr->ia_valid &= ~(ATTR_SIZE)) != 0) + nfserr = nfsd_setattr(rqstp, newfhp, attr); + } + +done: + fh_put(dirfhp); + RETURN(nfserr); +} + +static int +nfsd_proc_remove(struct svc_rqst *rqstp, struct nfsd_diropargs *argp, + void *resp) +{ + int nfserr; + + dprintk("nfsd: REMOVE %x/%ld %s\n", + SVCFH_DEV(&argp->fh), + SVCFH_INO(&argp->fh), + argp->name); + + /* Unlink. -SIFDIR means file must not be a directory */ + nfserr = nfsd_unlink(rqstp, &argp->fh, -S_IFDIR, argp->name, argp->len); + fh_put(&argp->fh); + RETURN(nfserr); +} + +static int +nfsd_proc_rename(struct svc_rqst *rqstp, struct nfsd_renameargs *argp, + void *resp) +{ + int nfserr; + + dprintk("nfsd: RENAME %x/%ld %s -> %x/%ld %s\n", + SVCFH_DEV(&argp->ffh), + SVCFH_INO(&argp->ffh), + argp->fname, + SVCFH_DEV(&argp->tfh), + SVCFH_INO(&argp->tfh), + argp->tname); + + nfserr = nfsd_rename(rqstp, &argp->ffh, argp->fname, argp->flen, + &argp->tfh, argp->tname, argp->tlen); + fh_put(&argp->ffh); + fh_put(&argp->tfh); + RETURN(nfserr); +} + +static int +nfsd_proc_link(struct svc_rqst *rqstp, struct nfsd_linkargs *argp, + void *resp) +{ + int nfserr; + + dprintk("nfsd: LINK %x/%ld -> %x/%ld %s\n", + SVCFH_DEV(&argp->ffh), + SVCFH_INO(&argp->ffh), + SVCFH_DEV(&argp->tfh), + SVCFH_INO(&argp->tfh), + argp->tname); + + nfserr = nfsd_link(rqstp, &argp->tfh, argp->tname, argp->tlen, + &argp->ffh); + fh_put(&argp->ffh); + fh_put(&argp->tfh); + RETURN(nfserr); +} + +static int +nfsd_proc_symlink(struct svc_rqst *rqstp, struct nfsd_symlinkargs *argp, + void *resp) +{ + struct svc_fh newfh; + int nfserr; + + dprintk("nfsd: SYMLINK %x/%ld %s -> %s\n", + SVCFH_DEV(&argp->ffh), + SVCFH_INO(&argp->ffh), + argp->fname, argp->tname); + + /* + * Create the link, look up new file and set attrs. + */ + nfserr = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen, + argp->tname, argp->tlen, + &newfh); + if (nfserr) + nfserr = nfsd_setattr(rqstp, &newfh, &argp->attrs); + + fh_put(&argp->ffh); + fh_put(&newfh); + RETURN(nfserr); +} + +/* + * Make directory. This operation is not idempotent. + */ +static int +nfsd_proc_mkdir(struct svc_rqst *rqstp, struct nfsd_createargs *argp, + struct nfsd_diropres *resp) +{ + int nfserr; + + dprintk("nfsd: MKDIR %x/%ld %s\n", + SVCFH_DEV(&argp->fh), + SVCFH_INO(&argp->fh), + argp->name); + + nfserr = nfsd_create(rqstp, &argp->fh, argp->name, argp->len, + &argp->attrs, S_IFDIR, 0, &resp->fh); + fh_put(&argp->fh); + RETURN(nfserr); +} + +/* + * Remove a directory + */ +static int +nfsd_proc_rmdir(struct svc_rqst *rqstp, struct nfsd_diropargs *argp, + void *resp) +{ + int nfserr; + + dprintk("nfsd: RMDIR %x/%ld %s\n", + SVCFH_DEV(&argp->fh), + SVCFH_INO(&argp->fh), + argp->name); + + nfserr = nfsd_unlink(rqstp, &argp->fh, S_IFDIR, argp->name, argp->len); + fh_put(&argp->fh); + RETURN(nfserr); +} + +/* + * Read a portion of a directory. + */ +static int +nfsd_proc_readdir(struct svc_rqst *rqstp, struct nfsd_readdirargs *argp, + struct nfsd_readdirres *resp) +{ + u32 * buffer; + int nfserr, count; + + dprintk("nfsd: READDIR %x/%ld %d bytes at %d\n", + SVCFH_DEV(&argp->fh), + SVCFH_INO(&argp->fh), + argp->count, argp->cookie); + + /* Reserve buffer space for status */ + svcbuf_reserve(&rqstp->rq_resbuf, &buffer, &count, 1); + + /* Make sure we've room for the NULL ptr & eof flag, and shrink to + * client read size */ + if ((count -= 8) > argp->count) + count = argp->count; + + /* Read directory and encode entries on the fly */ + nfserr = nfsd_readdir(rqstp, &argp->fh, (loff_t) argp->cookie, + nfssvc_encode_entry, + buffer, &count); + resp->count = count; + + fh_put(&argp->fh); + RETURN(nfserr); +} + +/* + * Get file system info + */ +static int +nfsd_proc_statfs(struct svc_rqst * rqstp, struct nfsd_fhandle *argp, + struct nfsd_statfsres *resp) +{ + int nfserr; + + dprintk("nfsd: STATFS %x/%ld\n", + SVCFH_DEV(&argp->fh), + SVCFH_INO(&argp->fh)); + + nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats); + fh_put(&argp->fh); + RETURN(nfserr); +} + +/* + * NFSv2 Server procedures. + * Only the results of non-idempotent operations are cached. + */ +#define nfsd_proc_none NULL +#define nfssvc_release_none NULL +struct nfsd_void { int dummy; }; + +#define PROC(name, argt, rest, relt, cache) \ + { (svc_procfunc) nfsd_proc_##name, \ + (kxdrproc_t) nfssvc_decode_##argt, \ + (kxdrproc_t) nfssvc_encode_##rest, \ + (kxdrproc_t) nfssvc_release_##relt, \ + sizeof(struct nfsd_##argt), \ + sizeof(struct nfsd_##rest), \ + 0, \ + cache \ + } +struct svc_procedure nfsd_procedures2[18] = { + PROC(null, void, void, none, RC_NOCACHE), + PROC(getattr, fhandle, attrstat, fhandle, RC_NOCACHE), + PROC(setattr, sattrargs, attrstat, fhandle, RC_REPLBUFF), + PROC(none, void, void, none, RC_NOCACHE), + PROC(lookup, diropargs, diropres, fhandle, RC_NOCACHE), + PROC(readlink, fhandle, readlinkres, none, RC_NOCACHE), + PROC(read, readargs, readres, fhandle, RC_NOCACHE), + PROC(none, void, void, none, RC_NOCACHE), + PROC(write, writeargs, attrstat, fhandle, RC_REPLBUFF), + PROC(create, createargs, diropres, fhandle, RC_REPLBUFF), + PROC(remove, diropargs, void, none, RC_REPLSTAT), + PROC(rename, renameargs, void, none, RC_REPLSTAT), + PROC(link, linkargs, void, none, RC_REPLSTAT), + PROC(symlink, symlinkargs, void, none, RC_REPLSTAT), + PROC(mkdir, createargs, diropres, fhandle, RC_REPLBUFF), + PROC(rmdir, diropargs, void, none, RC_REPLSTAT), + PROC(readdir, readdirargs, readdirres, none, RC_REPLSTAT), + PROC(statfs, fhandle, statfsres, none, RC_NOCACHE), +}; + + +/* + * Map errnos to NFS errnos. + */ +int +nfserrno (int errno) +{ + static struct { + int nfserr; + int syserr; + } nfs_errtbl[] = { + { NFS_OK, 0 }, + { NFSERR_PERM, EPERM }, + { NFSERR_NOENT, ENOENT }, + { NFSERR_IO, EIO }, + { NFSERR_NXIO, ENXIO }, + { NFSERR_ACCES, EACCES }, + { NFSERR_EXIST, EEXIST }, + { NFSERR_NODEV, ENODEV }, + { NFSERR_NOTDIR, ENOTDIR }, + { NFSERR_ISDIR, EISDIR }, + { NFSERR_INVAL, EINVAL }, + { NFSERR_FBIG, EFBIG }, + { NFSERR_NOSPC, ENOSPC }, + { NFSERR_ROFS, EROFS }, + { NFSERR_NAMETOOLONG, ENAMETOOLONG }, + { NFSERR_NOTEMPTY, ENOTEMPTY }, +#ifdef EDQUOT + { NFSERR_DQUOT, EDQUOT }, +#endif + { NFSERR_STALE, ESTALE }, + { NFSERR_WFLUSH, EIO }, + { -1, EIO } + }; + int i; + + for (i = 0; nfs_errtbl[i].nfserr != -1; i++) { + if (nfs_errtbl[i].syserr == errno) + return htonl (nfs_errtbl[i].nfserr); + } + printk (KERN_INFO "nfsd: non-standard errno: %d\n", errno); + return nfserr_io; +} + +#if 0 +static void +nfsd_dump(char *tag, u32 *buf, int len) +{ + int i; + + printk(KERN_NOTICE + "nfsd: %s (%d words)\n", tag, len); + + for (i = 0; i < len && i < 32; i += 8) + printk(KERN_NOTICE + " %08lx %08lx %08lx %08lx" + " %08lx %08lx %08lx %08lx\n", + buf[i], buf[i+1], buf[i+2], buf[i+3], + buf[i+4], buf[i+5], buf[i+6], buf[i+7]); +} +#endif diff -u --recursive --new-file v2.1.31/linux/fs/nfsd/nfssvc.c linux/fs/nfsd/nfssvc.c --- v2.1.31/linux/fs/nfsd/nfssvc.c Wed Dec 31 16:00:00 1969 +++ linux/fs/nfsd/nfssvc.c Fri Apr 4 12:48:15 1997 @@ -0,0 +1,245 @@ +/* + * linux/fs/nfsd/nfssvc.c + * + * Central processing for nfsd. + * + * Authors: Olaf Kirch (okir@monad.swb.de) + * + * Copyright (C) 1995, 1996, 1997 Olaf Kirch + */ + +#define __NO_VERSION__ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NFSDDBG_FACILITY NFSDDBG_SVC +#define NFSD_BUFSIZE (1024 + NFSSVC_MAXBLKSIZE) +#define BLOCKABLE_SIGS (~(_S(SIGKILL - 1) | _S(SIGSTOP - 1))) +#define _S(sig) (1 << ((sig) - 1)) + +extern struct svc_program nfsd_program; +static void nfsd(struct svc_rqst *rqstp); +struct timeval nfssvc_boot = { 0, 0 }; + +/* + * Make a socket for nfsd + */ +static int +nfsd_makesock(struct svc_serv *serv, int protocol, unsigned short port) +{ + struct sockaddr_in sin; + + dprintk("nfsd: creating socket proto = %d\n", protocol); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = INADDR_ANY; + sin.sin_port = htons(port); + return svc_create_socket(serv, protocol, &sin); +} + +int +nfsd_svc(unsigned short port, int nrservs) +{ + struct svc_serv * serv; + int error; + + dprintk("nfsd: creating service\n"); + if (nrservs < 0) + return -EINVAL; + if (nrservs > NFSD_MAXSERVS) + nrservs = NFSD_MAXSERVS; + + serv = svc_create(&nfsd_program, NFSD_BUFSIZE, NFSSVC_XDRSIZE); + if (serv == NULL) + return -ENOMEM; + + if ((error = nfsd_makesock(serv, IPPROTO_UDP, port)) < 0 + || (error = nfsd_makesock(serv, IPPROTO_TCP, port)) < 0) + goto failure; + + while (nrservs--) { + error = svc_create_thread(nfsd, serv); + if (error < 0) + break; + } + +failure: + svc_destroy(serv); /* Release server */ + return error; +} + +/* + * This is the NFS server kernel thread + */ +static void +nfsd(struct svc_rqst *rqstp) +{ + struct svc_serv *serv = rqstp->rq_server; + sigset_t oldsigmask; + int oldumask, err; + + lock_kernel(); + /* Lock module and set up kernel thread */ + MOD_INC_USE_COUNT; + exit_mm(current); + current->session = 1; + current->pgrp = 1; + sprintf(current->comm, "nfsd"); + + oldumask = current->fs->umask; /* Set umask to 0. */ + current->fs->umask = 0; + nfssvc_boot = xtime; /* record boot time */ + lockd_up(); /* start lockd */ + + /* + * The main request loop + */ + do { + /* + * Find a socket with data available and call its + * recvfrom routine. + */ + while ((err = svc_recv(serv, rqstp)) == -EAGAIN) + ; + if (err < 0) + break; + + /* Lock the export hash tables for reading. */ + exp_readlock(); + + /* Validate the client's address. This will also defeat + * port probes on port 2049 by unauthorized clients. + */ + rqstp->rq_client = exp_getclient(&rqstp->rq_addr); + if (!rqstp->rq_client) { + printk(KERN_WARNING "nfsd: unauthenticated request " + "from (%08lx:%d)\n", + ntohl(rqstp->rq_addr.sin_addr.s_addr), + ntohs(rqstp->rq_addr.sin_port)); + svc_drop(rqstp); + serv->sv_stats->rpcbadclnt++; + } else { + /* Process request with all signals blocked. */ + oldsigmask = current->blocked; + current->blocked = BLOCKABLE_SIGS; + svc_process(serv, rqstp); + current->blocked = oldsigmask; + } + + /* Unlock export hash tables */ + exp_unlock(); + } while (err >= 0); + + if (err != -EINTR) + printk(KERN_WARNING "nfsd: terminating on error %d\n", -err); + + /* Release lockd */ + lockd_down(); + + /* Destroy the thread */ + svc_exit_thread(rqstp); + current->fs->umask = oldumask; + + /* Release module */ + MOD_DEC_USE_COUNT; +} + +static int +nfsd_dispatch(struct svc_rqst *rqstp, u32 *statp) +{ + struct svc_procedure *proc; + kxdrproc_t xdr; + u32 nfserr; + + dprintk("nfsd_dispatch: proc %d\n", rqstp->rq_proc); + proc = rqstp->rq_procinfo; + + /* Check whether we have this call in the cache. */ + switch (nfsd_cache_lookup(rqstp, proc->pc_cachetype)) { + case RC_INTR: + case RC_DROPIT: + return 0; + case RC_REPLY: + return 1; + case RC_DOIT: + /* do it */ + } + + /* Decode arguments */ + xdr = proc->pc_decode; + if (xdr && !xdr(rqstp, rqstp->rq_argbuf.buf, rqstp->rq_argp)) { + dprintk("nfsd: failed to decode arguments!\n"); + nfsd_cache_update(rqstp, RC_NOCACHE, NULL); + *statp = rpc_garbage_args; + return 1; + } + + /* Now call the procedure handler, and encode NFS status. */ + nfserr = proc->pc_func(rqstp, rqstp->rq_argp, rqstp->rq_resp); + if (rqstp->rq_proc != 0) + svc_putlong(&rqstp->rq_resbuf, nfserr); + + /* Encode result. + * FIXME: Most NFSv3 calls return wcc data even when the call failed + */ + xdr = proc->pc_encode; + if (!nfserr && xdr + && !xdr(rqstp, rqstp->rq_resbuf.buf, rqstp->rq_resp)) { + /* Failed to encode result. Release cache entry */ + dprintk("nfsd: failed to encode result!\n"); + nfsd_cache_update(rqstp, RC_NOCACHE, NULL); + *statp = rpc_system_err; + return 1; + } + + /* Store reply in cache. */ + nfsd_cache_update(rqstp, proc->pc_cachetype, statp + 1); + return 1; +} + +static struct svc_version nfsd_version2 = { + 2, 18, nfsd_procedures2, nfsd_dispatch +}; +#ifdef CONFIG_NFSD_NFS3 +static struct svc_version nfsd_version3 = { + 3, 23, nfsd_procedures3, nfsd_dispatch +}; +#endif +static struct svc_version * nfsd_version[] = { + NULL, + NULL, + &nfsd_version2, +#ifdef CONFIG_NFSD_NFS3 + &nfsd_version3, +#endif +}; + +#define NFSD_NRVERS (sizeof(nfsd_version)/sizeof(nfsd_version[0])) +struct svc_program nfsd_program = { + NFS_PROGRAM, /* program number */ + 2, NFSD_NRVERS-1, /* version range */ + NFSD_NRVERS, /* nr of entries in nfsd_version */ + nfsd_version, /* version table */ + "nfsd", /* program name */ + &nfsd_svcstats, /* version table */ +}; diff -u --recursive --new-file v2.1.31/linux/fs/nfsd/nfsxdr.c linux/fs/nfsd/nfsxdr.c --- v2.1.31/linux/fs/nfsd/nfsxdr.c Wed Dec 31 16:00:00 1969 +++ linux/fs/nfsd/nfsxdr.c Fri Apr 4 11:06:02 1997 @@ -0,0 +1,470 @@ +/* + * linux/fs/nfsd/xdr.c + * + * XDR support for nfsd + * + * Copyright (C) 1995, 1996 Olaf Kirch + */ + +#include +#include +#include + +#include +#include +#include +#include + +#define NFSDDBG_FACILITY NFSDDBG_XDR + +u32 nfs_ok, nfserr_perm, nfserr_noent, nfserr_io, nfserr_nxio, + nfserr_acces, nfserr_exist, nfserr_nodev, nfserr_notdir, + nfserr_isdir, nfserr_fbig, nfserr_nospc, nfserr_rofs, + nfserr_nametoolong, nfserr_dquot, nfserr_stale; + +#ifdef NFSD_OPTIMIZE_SPACE +# define inline +#endif + +/* + * Mapping of S_IF* types to NFS file types + */ +static u32 nfs_ftypes[] = { + NFNON, NFFIFO, NFCHR, NFBAD, + NFDIR, NFBAD, NFBLK, NFBAD, + NFREG, NFBAD, NFLNK, NFBAD, + NFSOCK, NFBAD, NFLNK, NFBAD, +}; + +/* + * Initialization of NFS status variables + */ +void +nfsd_xdr_init(void) +{ + static int inited = 0; + + if (inited) + return; + + nfs_ok = htonl(NFS_OK); + nfserr_perm = htonl(NFSERR_PERM); + nfserr_noent = htonl(NFSERR_NOENT); + nfserr_io = htonl(NFSERR_IO); + nfserr_nxio = htonl(NFSERR_NXIO); + nfserr_acces = htonl(NFSERR_ACCES); + nfserr_exist = htonl(NFSERR_EXIST); + nfserr_nodev = htonl(NFSERR_NODEV); + nfserr_notdir = htonl(NFSERR_NOTDIR); + nfserr_isdir = htonl(NFSERR_ISDIR); + nfserr_fbig = htonl(NFSERR_FBIG); + nfserr_nospc = htonl(NFSERR_NOSPC); + nfserr_rofs = htonl(NFSERR_ROFS); + nfserr_nametoolong = htonl(NFSERR_NAMETOOLONG); + nfserr_dquot = htonl(NFSERR_DQUOT); + nfserr_stale = htonl(NFSERR_STALE); + + inited = 1; +} + +/* + * XDR functions for basic NFS types + */ +static inline u32 * +decode_fh(u32 *p, struct svc_fh *fhp) +{ + fh_init(fhp); + memcpy(&fhp->fh_handle, p, sizeof(struct knfs_fh)); + + /* FIXME: Look up export pointer here and verify + * Sun Secure RPC if requested */ + return p + (sizeof(struct knfs_fh) >> 2); +} + +static inline u32 * +encode_fh(u32 *p, struct svc_fh *fhp) +{ + memcpy(p, &fhp->fh_handle, sizeof(struct knfs_fh)); + return p + (sizeof(struct knfs_fh) >> 2); +} + +/* + * Decode a file name and make sure that the path contains + * no slashes or null bytes. + */ +static inline u32 * +decode_filename(u32 *p, char **namp, int *lenp) +{ + char *name; + int i; + + if ((p = xdr_decode_string(p, namp, lenp, NFS_MAXNAMLEN)) != NULL) { + for (i = 0, name = *namp; i < *lenp; i++, name++) { + if (*name == '\0' || *name == '/') + return NULL; + } + *name = '\0'; + } + + return p; +} + +static inline u32 * +decode_pathname(u32 *p, char **namp, int *lenp) +{ + char *name; + int i; + + if ((p = xdr_decode_string(p, namp, lenp, NFS_MAXPATHLEN)) != NULL) { + for (i = 0, name = *namp; i < *lenp; i++, name++) { + if (*name == '\0') + return NULL; + } + *name = '\0'; + } + + return p; +} + +static inline u32 * +decode_sattr(u32 *p, struct iattr *iap) +{ + u32 tmp, tmp1; + + iap->ia_valid = 0; + + /* Sun client bug compatibility check: some sun clients seem to + * put 0xffff in the mode field when they mean 0xffffffff. + * Quoting the 4.4BSD nfs server code: Nah nah nah nah na nah. + */ + if ((tmp = ntohl(*p++)) != (u32)-1 && tmp != 0xffff) { + iap->ia_valid |= ATTR_MODE; + iap->ia_mode = tmp; + } + if ((tmp = ntohl(*p++)) != (u32)-1) { + iap->ia_valid |= ATTR_UID; + iap->ia_uid = tmp; + } + if ((tmp = ntohl(*p++)) != (u32)-1) { + iap->ia_valid |= ATTR_GID; + iap->ia_gid = tmp; + } + if ((tmp = ntohl(*p++)) != (u32)-1) { + iap->ia_valid |= ATTR_SIZE; + iap->ia_size = tmp; + } + tmp = ntohl(*p++); tmp1 = ntohl(*p++); + if (tmp != (u32)-1 && tmp1 != (u32)-1) { + iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET; + iap->ia_atime = tmp; + } + tmp = ntohl(*p++); tmp1 = ntohl(*p++); + if (tmp != (u32)-1 && tmp1 != (u32)-1) { + iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET; + iap->ia_mtime = tmp; + } + return p; +} + +static inline u32 * +encode_fattr(struct svc_rqst *rqstp, u32 *p, struct inode *inode) +{ + if (!inode) + return 0; + *p++ = htonl(nfs_ftypes[(inode->i_mode & S_IFMT) >> 12]); + *p++ = htonl((u32) inode->i_mode); + *p++ = htonl((u32) inode->i_nlink); + *p++ = htonl((u32) nfsd_ruid(rqstp, inode->i_uid)); + *p++ = htonl((u32) nfsd_rgid(rqstp, inode->i_gid)); + if (S_ISLNK(inode->i_mode) && inode->i_size > NFS_MAXPATHLEN) { + *p++ = htonl(NFS_MAXPATHLEN); + } else { + *p++ = htonl((u32) inode->i_size); + } + *p++ = htonl((u32) inode->i_blksize); + *p++ = htonl((u32) inode->i_rdev); + *p++ = htonl((u32) inode->i_blocks); + *p++ = htonl((u32) inode->i_dev); + *p++ = htonl((u32) inode->i_ino); + *p++ = htonl((u32) inode->i_atime); + *p++ = 0; + *p++ = htonl((u32) inode->i_mtime); + *p++ = 0; + *p++ = htonl((u32) inode->i_ctime); + *p++ = 0; + + return p; +} + +/* + * Check buffer bounds after decoding arguments + */ +static inline int +xdr_argsize_check(struct svc_rqst *rqstp, u32 *p) +{ + struct svc_buf *buf = &rqstp->rq_argbuf; + + return p - buf->base <= buf->buflen; +} + +static inline int +xdr_ressize_check(struct svc_rqst *rqstp, u32 *p) +{ + struct svc_buf *buf = &rqstp->rq_resbuf; + + buf->len = p - buf->base; + dprintk("nfsd: ressize_check p %p base %p len %d\n", + p, buf->base, buf->buflen); + return (buf->len <= buf->buflen); +} + +/* + * XDR decode functions + */ +int +nfssvc_decode_void(struct svc_rqst *rqstp, u32 *p, void *dummy) +{ + return xdr_argsize_check(rqstp, p); +} + +int +nfssvc_decode_fhandle(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp) +{ + if (!(p = decode_fh(p, fhp))) + return 0; + return xdr_argsize_check(rqstp, p); +} + +int +nfssvc_decode_sattrargs(struct svc_rqst *rqstp, u32 *p, + struct nfsd_sattrargs *args) +{ + if (!(p = decode_fh(p, &args->fh)) + || !(p = decode_sattr(p, &args->attrs))) + return 0; + + return xdr_argsize_check(rqstp, p); +} + +int +nfssvc_decode_diropargs(struct svc_rqst *rqstp, u32 *p, + struct nfsd_diropargs *args) +{ + if (!(p = decode_fh(p, &args->fh)) + || !(p = decode_filename(p, &args->name, &args->len))) + return 0; + + return xdr_argsize_check(rqstp, p); +} + +int +nfssvc_decode_readargs(struct svc_rqst *rqstp, u32 *p, + struct nfsd_readargs *args) +{ + if (!(p = decode_fh(p, &args->fh))) + return 0; + + args->offset = ntohl(*p++); + args->count = ntohl(*p++); + args->totalsize = ntohl(*p++); + + return xdr_argsize_check(rqstp, p); +} + +int +nfssvc_decode_writeargs(struct svc_rqst *rqstp, u32 *p, + struct nfsd_writeargs *args) +{ + if (!(p = decode_fh(p, &args->fh))) + return 0; + + p++; /* beginoffset */ + args->offset = ntohl(*p++); /* offset */ + p++; /* totalcount */ + args->len = ntohl(*p++); + args->data = (char *) p; + p += XDR_QUADLEN(args->len); + + return xdr_argsize_check(rqstp, p); +} + +int +nfssvc_decode_createargs(struct svc_rqst *rqstp, u32 *p, + struct nfsd_createargs *args) +{ + if (!(p = decode_fh(p, &args->fh)) + || !(p = decode_filename(p, &args->name, &args->len)) + || !(p = decode_sattr(p, &args->attrs))) + return 0; + + return xdr_argsize_check(rqstp, p); +} + +int +nfssvc_decode_renameargs(struct svc_rqst *rqstp, u32 *p, + struct nfsd_renameargs *args) +{ + if (!(p = decode_fh(p, &args->ffh)) + || !(p = decode_filename(p, &args->fname, &args->flen)) + || !(p = decode_fh(p, &args->tfh)) + || !(p = decode_filename(p, &args->tname, &args->tlen))) + return 0; + + return xdr_argsize_check(rqstp, p); +} + +int +nfssvc_decode_linkargs(struct svc_rqst *rqstp, u32 *p, + struct nfsd_linkargs *args) +{ + if (!(p = decode_fh(p, &args->ffh)) + || !(p = decode_fh(p, &args->tfh)) + || !(p = decode_filename(p, &args->tname, &args->tlen))) + return 0; + + return xdr_argsize_check(rqstp, p); +} + +int +nfssvc_decode_symlinkargs(struct svc_rqst *rqstp, u32 *p, + struct nfsd_symlinkargs *args) +{ + if (!(p = decode_fh(p, &args->ffh)) + || !(p = decode_filename(p, &args->fname, &args->flen)) + || !(p = decode_pathname(p, &args->tname, &args->tlen)) + || !(p = decode_sattr(p, &args->attrs))) + return 0; + + return xdr_argsize_check(rqstp, p); +} + +int +nfssvc_decode_readdirargs(struct svc_rqst *rqstp, u32 *p, + struct nfsd_readdirargs *args) +{ + if (!(p = decode_fh(p, &args->fh))) + return 0; + args->cookie = ntohl(*p++); + args->count = ntohl(*p++); + + return xdr_argsize_check(rqstp, p); +} + +/* + * XDR encode functions + */ +int +nfssvc_encode_void(struct svc_rqst *rqstp, u32 *p, void *dummy) +{ + return xdr_ressize_check(rqstp, p); +} + +int +nfssvc_encode_attrstat(struct svc_rqst *rqstp, u32 *p, + struct nfsd_attrstat *resp) +{ + if (!(p = encode_fattr(rqstp, p, resp->fh.fh_inode))) + return 0; + return xdr_ressize_check(rqstp, p); +} + +int +nfssvc_encode_diropres(struct svc_rqst *rqstp, u32 *p, + struct nfsd_diropres *resp) +{ + if (!(p = encode_fh(p, &resp->fh)) + || !(p = encode_fattr(rqstp, p, resp->fh.fh_inode))) + return 0; + return xdr_ressize_check(rqstp, p); +} + +int +nfssvc_encode_readlinkres(struct svc_rqst *rqstp, u32 *p, + struct nfsd_readlinkres *resp) +{ + *p++ = htonl(resp->len); + p += XDR_QUADLEN(resp->len); + return xdr_ressize_check(rqstp, p); +} + +int +nfssvc_encode_readres(struct svc_rqst *rqstp, u32 *p, + struct nfsd_readres *resp) +{ + if (!(p = encode_fattr(rqstp, p, resp->fh.fh_inode))) + return 0; + *p++ = htonl(resp->count); + p += XDR_QUADLEN(resp->count); + + return xdr_ressize_check(rqstp, p); +} + +int +nfssvc_encode_readdirres(struct svc_rqst *rqstp, u32 *p, + struct nfsd_readdirres *resp) +{ + p += XDR_QUADLEN(resp->count); + return xdr_ressize_check(rqstp, p); +} + +int +nfssvc_encode_statfsres(struct svc_rqst *rqstp, u32 *p, + struct nfsd_statfsres *resp) +{ + struct statfs *stat = &resp->stats; + + *p++ = htonl(8 * 1024); /* max transfer size */ + *p++ = htonl(stat->f_bsize); + *p++ = htonl(stat->f_blocks); + *p++ = htonl(stat->f_bfree); + *p++ = htonl(stat->f_bavail); + return xdr_ressize_check(rqstp, p); +} + +int +nfssvc_encode_entry(struct readdir_cd *cd, const char *name, + int namlen, off_t offset, ino_t ino) +{ + u32 *p = cd->buffer; + int buflen, slen; + + /* + dprintk("nfsd: entry(%.*s off %ld ino %ld)\n", + namlen, name, offset, ino); + */ + + if (offset > ~((u32) 0)) + return -EINVAL; + if (cd->offset) + *cd->offset = htonl(offset); + if (namlen > NFS2_MAXNAMLEN) + namlen = NFS2_MAXNAMLEN;/* truncate filename */ + + slen = XDR_QUADLEN(namlen); + if ((buflen = cd->buflen - slen - 4) < 0) { + cd->eob = 1; + return -EINVAL; + } + *p++ = xdr_one; /* mark entry present */ + *p++ = htonl((u32) ino); /* file id */ + *p++ = htonl((u32) namlen); /* name length & name */ + memcpy(p, name, namlen); + p += slen; + cd->offset = p; /* remember pointer */ + *p++ = ~(u32) 0; /* offset of next entry */ + + cd->buflen = buflen; + cd->buffer = p; + return 0; +} + +/* + * XDR release functions + */ +int +nfssvc_release_fhandle(struct svc_rqst *rqstp, u32 *p, + struct nfsd_fhandle *resp) +{ + fh_put(&resp->fh); + return 1; +} diff -u --recursive --new-file v2.1.31/linux/fs/nfsd/stats.c linux/fs/nfsd/stats.c --- v2.1.31/linux/fs/nfsd/stats.c Wed Dec 31 16:00:00 1969 +++ linux/fs/nfsd/stats.c Fri Apr 4 11:06:02 1997 @@ -0,0 +1,92 @@ +/* + * nfsstat.c procfs-based user access to knfsd statistics + * + * /proc/net/nfssrv + * Format: + * net + * rpc + * auth + * fh + * rc + * proto + * + * ... (for each procedure and protocol version) + * + * Copyright (C) 1995, 1996 Olaf Kirch + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +struct nfsd_stats nfsdstats; + +static int nfsd_get_info(char *, char **, off_t, int, int); + +#ifndef PROC_NET_NFSSRV +# define PROC_NET_NFSSRV 0 +#endif + +static struct proc_dir_entry proc_nfssrv = { + PROC_NET_NFSSRV, 4, "nfsd", + S_IFREG | S_IRUGO, 1, 0, 0, + 6, &proc_net_inode_operations, + nfsd_get_info +}; + +struct svc_stat nfsd_svcstats = { + NULL, &proc_nfssrv, &nfsd_program, +}; + +static int +nfsd_get_info(char *buffer, char **start, off_t offset, int length, int dummy) +{ + int len; + + len = sprintf(buffer, + "rc %d %d %d\n", + nfsdstats.rchits, + nfsdstats.rcmisses, + nfsdstats.rcnocache); + + /* + * Append generic nfsd RPC statistics + */ + if (offset >= len) { + offset -= len; + len = svcstat_get_info(&nfsd_svcstats, buffer, start, + offset, length); +#if 0 + } else if (len < length) { + len = svcstat_get_info(&nfsd_svcstats, buffer + len, start, + offset - len, length - len); +#endif + } + + if (offset >= len) { + *start = buffer; + return 0; + } + *start = buffer + offset; + if ((len -= offset) > length) + len = length; + return len; +} + +void +nfsd_stat_init(void) +{ + svcstat_register(&nfsd_svcstats); +} + +void +nfsd_stat_shutdown(void) +{ + svcstat_unregister(&nfsd_svcstats); +} diff -u --recursive --new-file v2.1.31/linux/fs/nfsd/vfs.c linux/fs/nfsd/vfs.c --- v2.1.31/linux/fs/nfsd/vfs.c Wed Dec 31 16:00:00 1969 +++ linux/fs/nfsd/vfs.c Fri Apr 4 11:06:02 1997 @@ -0,0 +1,1095 @@ +/* + * linux/fs/nfsd/vfs.c + * + * File operations used by nfsd. Some of these have been ripped from + * other parts of the kernel because they weren't in ksyms.c, others + * are partial duplicates with added or changed functionality. + * + * Note that several functions lock the inode upon which they want + * to act, most notably those that create directory entries. The + * unlock operation can take place either by calling fh_unlock within + * the function directly, or at a later time in fh_put(). So if you + * notice code paths that apparently fail to unlock the inode, don't + * worry--they have been taken care of. + * + * Copyright (C) 1995, 1996, 1997 Olaf Kirch + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#if LINUX_VERSION_CODE >= 0x020100 +#include +#endif + +#define NFSDDBG_FACILITY NFSDDBG_FILEOP + +/* Symbol not exported */ +static struct super_block *get_super(dev_t dev); + +/* Open mode for nfsd_open */ +#define OPEN_READ 0 +#define OPEN_WRITE 1 + +/* Hack until we have a macro check for mandatory locks. */ +#ifndef IS_ISMNDLK +#define IS_ISMNDLK(i) (((i)->i_mode & (S_ISGID|S_ISVTX)) == S_ISGID) +#endif + +/* Check for dir entries '.' and '..' */ +#define isdotent(n, l) (l < 3 && n[0] == '.' && (l == 1 || n[1] == '.')) + +/* + * This is a cache of readahead params that help us choose the proper + * readahead strategy. Initially, we set all readahead parameters to 0 + * and let the VFS handle things. + * If you increase the number of cached files very much, you'll need to + * add a hash table here. + */ +struct raparms { + struct raparms * p_next; + unsigned int p_count; + dev_t p_dev; + ino_t p_ino; + unsigned long p_reada, + p_ramax, + p_raend, + p_ralen, + p_rawin; +}; + +#define FILECACHE_MAX (2 * NFSD_MAXSERVS) +static struct raparms raparms[FILECACHE_MAX]; +static struct raparms * raparm_cache = 0; + +/* + * Deny access to certain file systems + */ +static inline int +fs_off_limits(struct super_block *sb) +{ + return !sb || sb->s_magic == NFS_SUPER_MAGIC + || sb->s_magic == PROC_SUPER_MAGIC; +} + +/* + * Check whether directory is a mount point + */ +static inline int +nfsd_iscovered(struct inode *inode) +{ + return inode->i_mount != NULL; +} + +/* + * Look up one component of a pathname. + */ +int +nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, + int len, struct svc_fh *resfh) +{ + struct svc_export *exp; + struct super_block *sb; + struct inode *dirp, *inode; + int perm, err, dotdot = 0; + + dprintk("nfsd: nfsd_lookup(fh %x/%ld, %s)\n", + SVCFH_DEV(fhp), SVCFH_INO(fhp), name); + + /* Obtain inode and export */ + if ((err = fh_lookup(rqstp, fhp, S_IFDIR, MAY_NOP)) != 0) + return err; + dirp = fhp->fh_inode; + exp = fhp->fh_export; + + /* check permissions before traversing mount-points */ + perm = nfsd_permission(exp, dirp, MAY_EXEC); + + dotdot = (len == 2 && name[0] == '.' && name[1] == '.'); + if (dotdot) { + if (dirp == current->fs->root) { + dirp->i_count++; + *resfh = *fhp; + return 0; + } + + if (dirp->i_dev == exp->ex_dev && dirp->i_ino == exp->ex_ino) { + dirp->i_count++; + *resfh = *fhp; + return 0; + } + } else if (len == 1 && name[0] == '.') { + len = 0; + } else if (fs_off_limits(dirp->i_sb)) { + /* No lookups on NFS mounts and procfs */ + return nfserr_noent; + } else if (nfsd_iscovered(dirp)) { + /* broken NFS client */ + return nfserr_acces; + } + if (!dirp->i_op || !dirp->i_op->lookup) + return nfserr_notdir; + if (perm != 0) + return perm; + if (!len) { + dirp->i_count++; + *resfh = *fhp; + return 0; + } + + dirp->i_count++; /* lookup eats the dirp inode */ + err = dirp->i_op->lookup(dirp, name, len, &inode); + + if (err) + return nfserrno(-err); + + /* Note that lookup() has already done a call to iget() so that + * the inode returned never refers to an inode covered by a mount. + * When this has happened, return the covered inode. + */ + if (!dotdot && (sb = inode->i_sb) && (inode == sb->s_mounted)) { + iput(inode); + inode = sb->s_covered; + inode->i_count++; + } + + fh_compose(resfh, exp, inode); + return 0; +} + +/* + * Set various file attributes. + */ +int +nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap) +{ + struct inode *inode; + int accmode = MAY_SATTR; + int ftype = 0; + int imode; + int err; + + if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE)) + accmode |= MAY_WRITE; + if (iap->ia_valid & ATTR_SIZE) + ftype = S_IFREG; + + /* Get inode */ + if ((err = fh_lookup(rqstp, fhp, ftype, accmode)) != 0) + return err; + + fh_lock(fhp); /* lock inode */ + inode = fhp->fh_inode; + + /* The size case is special... */ + if ((iap->ia_valid & ATTR_SIZE) && S_ISREG(inode->i_mode)) { + if (iap->ia_size < inode->i_size) { + err = nfsd_permission(fhp->fh_export, inode, MAY_TRUNC); + if (err != 0) + return err; + } + if ((err = get_write_access(inode)) != 0) + return nfserrno(-err); + inode->i_size = iap->ia_size; + if (inode->i_op && inode->i_op->truncate) + inode->i_op->truncate(inode); + inode->i_dirt = 1; + put_write_access(inode); + iap->ia_valid &= ATTR_SIZE; + iap->ia_valid |= ATTR_MTIME; + iap->ia_mtime = CURRENT_TIME; + } + + imode = inode->i_mode; + if (iap->ia_valid & ATTR_MODE) { + iap->ia_mode &= S_IALLUGO; + imode = iap->ia_mode |= (imode & ~S_IALLUGO); + } + + /* Revoke setuid/setgid bit on chown/chgrp */ + if ((iap->ia_valid & ATTR_UID) && (imode & S_ISUID) + && iap->ia_uid != inode->i_uid) { + iap->ia_valid |= ATTR_MODE; + iap->ia_mode = imode &= ~S_ISUID; + } + if ((iap->ia_valid & ATTR_GID) && (imode & S_ISGID) + && iap->ia_gid != inode->i_gid) { + iap->ia_valid |= ATTR_MODE; + iap->ia_mode = imode &= ~S_ISGID; + } + + /* Change the attributes. */ + if (iap->ia_valid) { + iap->ia_valid |= ATTR_CTIME; + iap->ia_ctime = CURRENT_TIME; + err = nfsd_notify_change(inode, iap); + if (err) + return nfserrno(-err); + if (EX_ISSYNC(fhp->fh_export)) + nfsd_write_inode(inode); + } + + return 0; +} + +/* + * Open an existing file or directory. + * The wflag argument indicates write access. + */ +int +nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, + int wflag, struct file *filp) +{ + struct inode *inode; + int access, err; + + access = wflag? MAY_WRITE : MAY_READ; + if ((err = fh_lookup(rqstp, fhp, type, access)) != 0) + return err; + inode = fhp->fh_inode; + + /* Disallow access to files with the append-only bit set or + * with mandatory locking enabled */ + if (IS_APPEND(inode) || IS_ISMNDLK(inode)) + return nfserr_perm; + if (!inode->i_op || !inode->i_op->default_file_ops) + return nfserr_perm; + + if (wflag && (err = get_write_access(inode)) != 0) + return nfserrno(-err); + + memset(filp, 0, sizeof(*filp)); + filp->f_op = inode->i_op->default_file_ops; + filp->f_count = 1; + filp->f_flags = wflag? O_WRONLY : O_RDONLY; + filp->f_mode = wflag? FMODE_WRITE : FMODE_READ; + filp->f_inode = inode; + + if (filp->f_op->open) { + err = filp->f_op->open(inode, filp); + if (err) { + if (wflag) + put_write_access(inode); + filp->f_count--; + return nfserrno(-err); + } + } + + inode->i_count++; + return 0; +} + +/* + * Close a file. + */ +void +nfsd_close(struct file *filp) +{ + struct inode *inode; + + inode = filp->f_inode; + if (!inode->i_count) + printk(KERN_WARNING "nfsd: inode count == 0!\n"); + if (filp->f_op && filp->f_op->release) + filp->f_op->release(inode, filp); + if (filp->f_mode & FMODE_WRITE) + put_write_access(inode); + iput(inode); +} + +/* + * Sync a file + */ +void +nfsd_sync(struct inode *inode, struct file *filp) +{ + filp->f_op->fsync(inode, filp); +} + +/* + * Obtain the readahead parameters for the given file + */ +static inline struct raparms * +nfsd_get_raparms(dev_t dev, ino_t ino) +{ + struct raparms *ra, **rap, **frap = NULL; + + for (rap = &raparm_cache; (ra = *rap); rap = &ra->p_next) { + if (ra->p_dev != dev || ra->p_ino != ino) { + if (ra->p_count == 0) + frap = rap; + } else + goto found; + } + if (!frap) + return NULL; + rap = frap; + ra = *frap; + memset(ra, 0, sizeof(*ra)); +found: + if (rap != &raparm_cache) { + *rap = ra->p_next; + ra->p_next = raparm_cache; + raparm_cache = ra; + } + ra->p_count++; + return ra; +} + +/* + * Read data from a file. count must contain the requested read count + * on entry. On return, *count contains the number of bytes actually read. + */ +int +nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, char *buf, + unsigned long *count) +{ + struct raparms *ra; + struct inode *inode; + struct file file; + unsigned long oldfs; + int err; + + if ((err = nfsd_open(rqstp, fhp, S_IFREG, OPEN_READ, &file)) != 0) + return err; + inode = file.f_inode; + if (!file.f_op->read) { + nfsd_close(&file); + return nfserr_perm; + } + + /* Get readahead parameters */ + if ((ra = nfsd_get_raparms(inode->i_dev, inode->i_ino)) != NULL) { + file.f_reada = ra->p_reada; + file.f_ramax = ra->p_ramax; + file.f_raend = ra->p_raend; + file.f_ralen = ra->p_ralen; + file.f_rawin = ra->p_rawin; + } + file.f_pos = offset; + + oldfs = get_fs(); set_fs(KERNEL_DS); + err = file.f_op->read(file.f_inode, &file, buf, *count); + set_fs(oldfs); + + /* Write back readahead params */ + if (ra != NULL) { + dprintk("nfsd: raparms %ld %ld %ld %ld %ld\n", + file.f_reada, file.f_ramax, file.f_raend, + file.f_ralen, file.f_rawin); + ra->p_reada = file.f_reada; + ra->p_ramax = file.f_ramax; + ra->p_raend = file.f_raend; + ra->p_ralen = file.f_ralen; + ra->p_rawin = file.f_rawin; + ra->p_count -= 1; + } + + nfsd_close(&file); + + if (err < 0) + return nfserrno(-err); + *count = err; + return 0; +} + +/* + * Write data to a file. + * The stable flag requests synchronous writes. + */ +int +nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, + char *buf, unsigned long cnt, int stable) +{ + struct svc_export *exp; + struct file file; + struct inode *inode; + unsigned long oldfs; + int err; + + if (!cnt) + return 0; + if ((err = nfsd_open(rqstp, fhp, S_IFREG, OPEN_WRITE, &file)) != 0) + return err; + if (!file.f_op->write) { + nfsd_close(&file); + return nfserr_perm; + } + + inode = fhp->fh_inode; + exp = fhp->fh_export; + + /* + * Request sync writes if + * - the sync export option has been set, or + * - the client requested O_SYNC behavior (NFSv3 feature). + * When gathered writes have been configured for this volume, + * flushing the data to disk is handled separately below. + */ + if ((stable || (stable = EX_ISSYNC(exp))) && !EX_WGATHER(exp)) + file.f_flags |= O_SYNC; + + fh_lock(fhp); /* lock inode */ + file.f_pos = offset; /* set write offset */ + + /* Write the data. */ + oldfs = get_fs(); set_fs(KERNEL_DS); + err = file.f_op->write(inode, &file, buf, cnt); + set_fs(oldfs); + + /* clear setuid/setgid flag after write */ + if (err >= 0 && (inode->i_mode & (S_ISUID | S_ISGID))) { + struct iattr ia; + + ia.ia_valid = ATTR_MODE; + ia.ia_mode = inode->i_mode & ~(S_ISUID | S_ISGID); + nfsd_notify_change(inode, &ia); + } + + fh_unlock(fhp); /* unlock inode */ + + if (err >= 0 && stable) { + static unsigned long last_ino = 0; + static kdev_t last_dev = NODEV; + + /* + * Gathered writes: If another process is currently + * writing to the file, there's a high chance + * this is another nfsd (triggered by a bulk write + * from a client's biod). Rather than syncing the + * file with each write request, we sleep for 10 msec. + * + * I don't know if this roughly approximates + * C. Juszak's idea of gathered writes, but it's a + * nice and simple solution (IMHO), and it seems to + * work:-) + */ + if (EX_WGATHER(exp) && (inode->i_writecount > 1 + || (last_ino == inode->i_ino && last_dev == inode->i_dev))) { +#if 0 + current->timeout = jiffies + 10 * HZ / 1000; + interruptible_sleep_on(&inode->i_wait); +#else + dprintk("nfsd: write defer %d\n", current->pid); + need_resched = 1; + current->timeout = jiffies + HZ / 100; + schedule(); + dprintk("nfsd: write resume %d\n", current->pid); +#endif + } + + if (inode->i_dirt) { + dprintk("nfsd: write sync %d\n", current->pid); + nfsd_sync(inode, &file); + nfsd_write_inode(inode); + } + wake_up(&inode->i_wait); + last_ino = inode->i_ino; + last_dev = inode->i_dev; + } + + nfsd_close(&file); + + dprintk("nfsd: write complete\n"); + return (err < 0)? nfserrno(-err) : 0; +} + +/* + * Create a file (regular, directory, device, fifo). + * UNIX sockets not yet implemented. + */ +int +nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, + char *fname, int flen, struct iattr *iap, + int type, dev_t rdev, struct svc_fh *resfhp) +{ + struct inode *dirp, *inode = NULL; + int err; + + if (!flen) + return nfserr_perm; + + if (!(iap->ia_valid & ATTR_MODE)) + iap->ia_mode = 0; + + if ((err = fh_lookup(rqstp, fhp, S_IFDIR, MAY_CREATE)) != 0) + return err; + + fh_lock(fhp); /* lock directory */ + dirp = fhp->fh_inode; + dirp->i_count++; /* dirop eats the inode */ + + switch (type) { + case S_IFREG: + if (!dirp->i_op || !dirp->i_op->create) + return nfserr_perm; + err = dirp->i_op->create(dirp, fname, flen, + iap->ia_mode, &inode); + break; + case S_IFDIR: + if (!dirp->i_op || !dirp->i_op->mkdir) + return nfserr_perm; + err = dirp->i_op->mkdir(dirp, fname, flen, iap->ia_mode); + break; + case S_IFCHR: + case S_IFBLK: + case S_IFIFO: + if (!dirp->i_op || !dirp->i_op->mknod) + return nfserr_perm; + err = dirp->i_op->mknod(dirp, fname, flen, iap->ia_mode, rdev); + break; + default: + iput(dirp); + err = -EACCES; + } + + fh_unlock(fhp); + + if (err < 0) + return nfserrno(-err); + + /* + * If the VFS call doesn't return the inode, look it up now. + */ + if (inode == NULL) { + dirp->i_count++; + err = dirp->i_op->lookup(dirp, fname, flen, &inode); + if (err < 0) + return -nfserrno(err); /* Huh?! */ + } + + if (EX_ISSYNC(fhp->fh_export)) + nfsd_write_inode(dirp); + + /* Assemble the file handle for the newly created file */ + fh_compose(resfhp, fhp->fh_export, inode); + + /* Set file attributes. Mode has already been set and + * setting uid/gid works only for root. Irix appears to + * send along the gid when it tries to implement setgid + * directories via NFS. + */ + if ((iap->ia_valid &= (ATTR_UID|ATTR_GID|ATTR_MODE)) != 0) { + if ((err = nfsd_setattr(rqstp, resfhp, iap)) != 0) { + fh_put(resfhp); + return err; + } + } + + return 0; +} + +/* + * Truncate a file. + * The calling routines must make sure to update the ctime + * field and call notify_change. + */ +int +nfsd_truncate(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned long size) +{ + struct inode *inode; + int err; + + if ((err = fh_lookup(rqstp, fhp, S_IFREG, MAY_WRITE|MAY_TRUNC)) != 0) + return err; + + fh_lock(fhp); /* lock inode if not yet locked */ + inode = fhp->fh_inode; + + if ((err = get_write_access(inode)) != 0) + return nfserrno(-err); + inode->i_size = size; + if (inode->i_op && inode->i_op->truncate) + inode->i_op->truncate(inode); + inode->i_dirt = 1; + put_write_access(inode); + + fh_unlock(fhp); + + return 0; +} + +/* + * Read a symlink. On entry, *lenp must contain the maximum path length that + * fits into the buffer. On return, it contains the true length. + */ +int +nfsd_readlink(struct svc_rqst *rqstp, struct svc_fh *fhp, char *buf, int *lenp) +{ + struct inode *inode; + unsigned long oldfs; + int err; + + if ((err = fh_lookup(rqstp, fhp, S_IFLNK, MAY_READ)) != 0) + return err; + inode = fhp->fh_inode; + + if (!inode->i_op || !inode->i_op->readlink) + return nfserr_io; + + inode->i_count++; + oldfs = get_fs(); set_fs(KERNEL_DS); + err = inode->i_op->readlink(inode, buf, *lenp); + set_fs(oldfs); + + if (err < 0) + return nfserrno(-err); + *lenp = err; + + return 0; +} + +/* + * Create a symlink and look up its inode + */ +int +nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp, + char *fname, int flen, + char *path, int plen, + struct svc_fh *resfhp) +{ + struct inode *dirp, *inode; + int err; + + if (!flen || !plen) + return nfserr_noent; + + if ((err = fh_lookup(rqstp, fhp, S_IFDIR, MAY_CREATE)) != 0) + return err; + + dirp = fhp->fh_inode; + if (nfsd_iscovered(dirp)) + return nfserr_perm; + if (!dirp->i_op || !dirp->i_op->symlink) + return nfserr_perm; + + fh_lock(fhp); /* lock inode */ + dirp->i_count++; + err = dirp->i_op->symlink(dirp, fname, flen, path); + fh_unlock(fhp); /* unlock inode */ + + if (err) + return nfserrno(-err); + + if (EX_ISSYNC(fhp->fh_export)) + nfsd_write_inode(dirp); + + /* + * Okay, now look up the inode of the new symlink. + */ + dirp->i_count++; /* lookup eats the dirp inode */ + err = dirp->i_op->lookup(dirp, fname, flen, &inode); + if (err) + return nfserrno(-err); + + fh_compose(resfhp, fhp->fh_export, inode); + return 0; +} + +/* + * Create a hardlink + */ +int +nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp, + char *fname, int len, struct svc_fh *tfhp) +{ + struct inode *dirp, *dest; + int err; + + if ((err = fh_lookup(rqstp, ffhp, S_IFDIR, MAY_CREATE) != 0) || + (err = fh_lookup(rqstp, tfhp, S_IFREG, MAY_NOP)) != 0) + return err; + dirp = ffhp->fh_inode; + dest = tfhp->fh_inode; + + if (!len) + return nfserr_perm; + if (nfsd_iscovered(dirp)) + return nfserr_acces; + if (dirp->i_dev != dest->i_dev) + return nfserr_acces; /* FIXME: nxdev for NFSv3 */ + if (IS_IMMUTABLE(dest) /* || IS_APPEND(dest) */ ) + return nfserr_perm; + if (!dirp->i_op || !dirp->i_op->link) + return nfserr_perm; + + fh_lock(ffhp); /* lock directory inode */ + dirp->i_count++; + err = dirp->i_op->link(dest, dirp, fname, len); + fh_unlock(ffhp); /* unlock inode */ + + if (!err && EX_ISSYNC(ffhp->fh_export)) { + nfsd_write_inode(dirp); + nfsd_write_inode(dest); + } + + return err? nfserrno(-err) : 0; +} + +/* + * Rename a file + */ +int +nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, + struct svc_fh *tfhp, char *tname, int tlen) +{ + struct inode *fdir, *tdir; + int err; + + if ((err = fh_lookup(rqstp, ffhp, S_IFDIR, MAY_REMOVE) != 0) + || (err = fh_lookup(rqstp, tfhp, S_IFDIR, MAY_CREATE)) != 0) + return err; + fdir = ffhp->fh_inode; + tdir = tfhp->fh_inode; + + if (!flen || (fname[0] == '.' && + (flen == 1 || (flen == 2 && fname[1] == '.'))) || + !tlen || (tname[0] == '.' && + (tlen == 1 || (tlen == 2 && tname[1] == '.')))) + return nfserr_perm; + + if (fdir->i_dev != tdir->i_dev) + return nfserr_acces; /* nfserr_nxdev */ + if (!fdir->i_op || !fdir->i_op->rename) + return nfserr_perm; + + fh_lock(tfhp); /* lock destination directory */ + tdir->i_count++; + fdir->i_count++; + err = fdir->i_op->rename(fdir, fname, flen, tdir, tname, tlen, 0); + fh_unlock(tfhp); /* unlock inode */ + + if (!err && EX_ISSYNC(tfhp->fh_export)) { + nfsd_write_inode(fdir); + nfsd_write_inode(tdir); + } + + return err? nfserrno(-err) : 0; +} + +/* + * Unlink a file or directory + */ +int +nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, + char *fname, int flen) +{ + struct inode *dirp; + int err; + + if (!flen || isdotent(fname, flen)) + return nfserr_acces; + + if ((err = fh_lookup(rqstp, fhp, S_IFDIR, MAY_REMOVE)) != 0) + return err; + + fh_lock(fhp); /* lock inode */ + dirp = fhp->fh_inode; + + if (type == S_IFDIR) { + if (!dirp->i_op || !dirp->i_op->rmdir) + return nfserr_notdir; + dirp->i_count++; + err = dirp->i_op->rmdir(dirp, fname, flen); + } else { /* other than S_IFDIR */ + if (!dirp->i_op || !dirp->i_op->unlink) + return nfserr_perm; + dirp->i_count++; + err = dirp->i_op->unlink(dirp, fname, flen); + } + + fh_unlock(fhp); /* unlock inode */ + if (!err && EX_ISSYNC(fhp->fh_export)) + nfsd_write_inode(dirp); + + return err? nfserrno(-err) : 0; +} + +/* + * Read entries from a directory. + */ +int +nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, + encode_dent_fn func, u32 *buffer, int *countp) +{ + struct readdir_cd cd; + struct file file; + u32 *p; + int oldlen, eof, err; + + if (offset > ~(u32) 0) + return 0; + + if ((err = nfsd_open(rqstp, fhp, S_IFDIR, OPEN_READ, &file)) != 0) + return nfserrno(-err); + + if (!file.f_op->readdir) { + nfsd_close(&file); + return nfserr_notdir; + } + file.f_pos = offset; + + /* Set up the readdir context */ + memset(&cd, 0, sizeof(cd)); + cd.rqstp = rqstp; + cd.buffer = buffer; + cd.buflen = *countp >> 2; + + /* + * Read the directory entries. This silly loop is necessary because + * readdir() is not guaranteed to fill up the entire buffer, but + * may choose to do less. + */ + do { + oldlen = cd.buflen; + + /* + dprintk("nfsd: f_op->readdir(%x/%ld @ %d) buflen = %d (%d)\n", + file.f_inode->i_dev, file.f_inode->i_ino, + (int) file.f_pos, (int) oldlen, (int) cd.buflen); + */ + err = file.f_op->readdir(file.f_inode, &file, + &cd, (filldir_t) func); + + if (err < 0) { + nfsd_close(&file); + return nfserrno(-err); + } + if (oldlen == cd.buflen) + break; + } while (oldlen != cd.buflen && !cd.eob); + + /* If we didn't fill the buffer completely, we're at EOF */ + eof = !cd.eob; + + /* Hewlett Packard ignores the eof flag on READDIR. Some + * fs-specific readdir implementations seem to reset f_pos to 0 + * at EOF however, causing an endless loop. */ + if (cd.offset && !eof) + *cd.offset = htonl(file.f_pos); + + /* Close the file */ + nfsd_close(&file); + + p = cd.buffer; + *p++ = 0; /* no more entries */ + *p++ = htonl(eof); /* end of directory */ + *countp = (caddr_t) p - (caddr_t) buffer; + + dprintk("nfsd: readdir result %d bytes, eof %d offset %ld\n", + *countp, eof, + cd.offset? ntohl(*cd.offset) : -1); + return 0; +} + +/* + * Get file system stats + */ +int +nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct statfs *stat) +{ + struct inode *inode; + struct super_block *sb; + unsigned long oldfs; + int err; + + if ((err = fh_lookup(rqstp, fhp, 0, MAY_NOP)) != 0) + return err; + inode = fhp->fh_inode; + + if (!(sb = inode->i_sb) || !sb->s_op->statfs) + return nfserr_io; + + oldfs = get_fs(); + set_fs (KERNEL_DS); + sb->s_op->statfs(sb, stat, sizeof(*stat)); + set_fs (oldfs); + + return 0; +} + +/* + * Check for a user's access permissions to this inode. + */ +int +nfsd_permission(struct svc_export *exp, struct inode *inode, int acc) +{ + int err; + + if (acc == MAY_NOP) + return 0; + + /* + dprintk("nfsd: permission 0x%x%s%s%s%s%s mode 0%o%s%s%s\n", + acc, + (acc & MAY_READ)? " read" : "", + (acc & MAY_WRITE)? " write" : "", + (acc & MAY_EXEC)? " exec" : "", + (acc & MAY_SATTR)? " sattr" : "", + (acc & MAY_TRUNC)? " trunc" : "", + inode->i_mode, + IS_IMMUTABLE(inode)? " immut" : "", + IS_APPEND(inode)? " append" : "", + IS_RDONLY(inode)? " ro" : ""); + dprintk(" owner %d/%d user %d/%d\n", + inode->i_uid, inode->i_gid, current->fsuid, current->fsgid); + */ + + if (acc & (MAY_WRITE | MAY_SATTR | MAY_TRUNC)) { + if (EX_RDONLY(exp) || IS_RDONLY(inode)) + return nfserr_rofs; + if (S_ISDIR(inode->i_mode) && nfsd_iscovered(inode)) + return nfserr_perm; + if (/* (acc & MAY_WRITE) && */ IS_IMMUTABLE(inode)) + return nfserr_perm; + } + if ((acc & MAY_TRUNC) && IS_APPEND(inode)) + return nfserr_perm; + + /* + * The file owner always gets access permission. This is to make + * file access work even when the client has done a fchmod(fd, 0). + * + * However, `cp foo bar' should fail nevertheless when bar is + * readonly. A sensible way to do this might be to reject all + * attempts to truncate a read-only file, because a creat() call + * always implies file truncation. + */ + if (inode->i_uid == current->fsuid /* && !(acc & MAY_TRUNC) */) + return 0; + + err = permission(inode, acc & (MAY_READ|MAY_WRITE|MAY_EXEC)); + + /* Allow read access to binaries even when mode 111 */ + if (err == -EPERM && S_ISREG(inode->i_mode) && acc == MAY_READ) + err = permission(inode, MAY_EXEC); + + return err? nfserrno(-err) : 0; +} + +/* + * Look up the inode for a given FH. + */ +struct inode * +nfsd_iget(dev_t dev, ino_t ino) +{ + struct super_block *sb; + + if (!(sb = get_super(dev)) || fs_off_limits(sb)) + return NULL; + return __iget(sb, ino, 0); +} + +/* + * Write the inode if dirty (copy of fs/inode.c:write_inode) + */ +void +nfsd_write_inode(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + + if (!inode->i_dirt) + return; + while (inode->i_lock) { + sleep_on(&inode->i_wait); + if (!inode->i_dirt) + return; + } + if (!sb || !sb->s_op || !sb->s_op->write_inode) { + inode->i_dirt = 0; + return; + } + inode->i_lock = 1; + sb->s_op->write_inode(inode); + inode->i_lock = 0; + wake_up(&inode->i_wait); +} + +/* + * Look up the root inode of the parent fs. + * We have to go through iget in order to allow for wait_on_inode. + */ +int +nfsd_parentdev(dev_t* devp) +{ + struct super_block *sb; + + if (!(sb = get_super(*devp)) || !sb->s_covered) + return 0; + if (*devp == sb->s_covered->i_dev) + return 0; + *devp = sb->s_covered->i_dev; + return 1; +} + +/* Duplicated here from fs/super.c because it's not exported */ +static struct super_block * +get_super(dev_t dev) +{ + struct super_block *s; + + if (!dev) + return NULL; + s = 0 + super_blocks; + while (s < NR_SUPER + super_blocks) + if (s->s_dev == dev) { + wait_on_super(s); + if (s->s_dev == dev) + return s; + s = 0 + super_blocks; + } else + s++; + return NULL; +} + +/* + * This is a copy from fs/inode.c because it wasn't exported. + */ +int +nfsd_notify_change(struct inode *inode, struct iattr *attr) +{ + int retval; + + if (inode->i_sb && inode->i_sb->s_op && + inode->i_sb->s_op->notify_change) + return inode->i_sb->s_op->notify_change(inode, attr); + + if ((retval = inode_change_ok(inode, attr)) != 0) + return retval; + + inode_setattr(inode, attr); + return 0; +} + +/* + * Initialize readahead param cache + */ +void +nfsd_racache_init(void) +{ + int i; + + if (raparm_cache) + return; + memset(raparms, 0, sizeof(raparms)); + for (i = 0; i < FILECACHE_MAX - 1; i++) { + raparms[i].p_next = raparms + i + 1; + } + raparm_cache = raparms; +} diff -u --recursive --new-file v2.1.31/linux/fs/proc/root.c linux/fs/proc/root.c --- v2.1.31/linux/fs/proc/root.c Mon Mar 17 14:54:31 1997 +++ linux/fs/proc/root.c Sat Apr 5 12:14:20 1997 @@ -137,6 +137,7 @@ }; #endif +#ifdef CONFIG_SYSCTL struct proc_dir_entry proc_sys_root = { PROC_SYS, 3, "sys", /* inode, name */ S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0, /* mode, nlink, uid, gid */ @@ -145,6 +146,7 @@ NULL, /* next */ NULL, NULL /* parent, subdir */ }; +#endif #if defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE) @@ -556,7 +558,9 @@ proc_register(&proc_root, &proc_root_self); proc_net = create_proc_entry("net", S_IFDIR, 0); proc_scsi = create_proc_entry("scsi", S_IFDIR, 0); +#ifdef CONFIG_SYSCTL proc_register(&proc_root, &proc_sys_root); +#endif #ifdef CONFIG_MCA proc_register(&proc_root, &proc_mca); #endif diff -u --recursive --new-file v2.1.31/linux/fs/smbfs/sock.c linux/fs/smbfs/sock.c --- v2.1.31/linux/fs/smbfs/sock.c Thu Mar 27 14:40:06 1997 +++ linux/fs/smbfs/sock.c Fri Apr 4 09:34:31 1997 @@ -27,7 +27,7 @@ static int _recvfrom(struct socket *sock, unsigned char *ubuf, int size, - int noblock, unsigned flags) + unsigned flags) { struct iovec iov; struct msghdr msg; @@ -41,9 +41,6 @@ iov.iov_base = ubuf; iov.iov_len = size; - if (noblock) { - flags |= MSG_DONTWAIT; - } memset(&scm, 0,sizeof(scm)); size=sock->ops->recvmsg(sock, &msg, size, flags, &scm); if(size>=0) @@ -52,8 +49,7 @@ } static int -_send(struct socket *sock, const void *buff, int len, - int noblock, unsigned flags) +_send(struct socket *sock, const void *buff, int len) { struct iovec iov; struct msghdr msg; @@ -69,12 +65,8 @@ iov.iov_base = (void *)buff; iov.iov_len = len; - - if (noblock) - flags |= MSG_DONTWAIT; - - msg.msg_flags = flags; + msg.msg_flags = 0; err = scm_send(sock, &msg, &scm); if (err < 0) @@ -98,12 +90,14 @@ fs = get_fs(); set_fs(get_ds()); - result = _recvfrom(sock, (void *) peek_buf, 1, 1, MSG_PEEK); + result = _recvfrom(sock, (void *) peek_buf, 1, + MSG_PEEK | MSG_DONTWAIT); while ((result != -EAGAIN) && (peek_buf[0] == 0x85)) { /* got SESSION KEEP ALIVE */ - result = _recvfrom(sock, (void *) peek_buf, 4, 1, 0); + result = _recvfrom(sock, (void *) peek_buf, 4, + MSG_DONTWAIT); DDPRINTK("smb_data_callback:" " got SESSION KEEP ALIVE\n"); @@ -112,8 +106,8 @@ { break; } - result = _recvfrom(sock, (void *) peek_buf, - 1, 1, MSG_PEEK); + result = _recvfrom(sock, (void *) peek_buf, 1, + MSG_PEEK | MSG_DONTWAIT); } set_fs(fs); @@ -233,7 +227,7 @@ { result = _send(sock, (void *) (source + already_sent), - length - already_sent, 0, 0); + length - already_sent); if (result == 0) { @@ -260,7 +254,7 @@ { result = _recvfrom(sock, (void *) (target + already_read), - length - already_read, 0, 0); + length - already_read, 0); if (result == 0) { @@ -393,7 +387,6 @@ int total_data = 0; int total_param = 0; int result; - unsigned char *inbuf = server->packet; unsigned char *rcv_buf; int buf_len; int data_len = 0; @@ -409,8 +402,8 @@ *ldata = *lparam = 0; return 0; } - total_data = WVAL(inbuf, smb_tdrcnt); - total_param = WVAL(inbuf, smb_tprcnt); + total_data = WVAL(server->packet, smb_tdrcnt); + total_param = WVAL(server->packet, smb_tprcnt); DDPRINTK("smb_receive_trans2: td=%d,tp=%d\n", total_data, total_param); @@ -435,6 +428,8 @@ while (1) { + unsigned char *inbuf = server->packet; + if (WVAL(inbuf, smb_prdisp) + WVAL(inbuf, smb_prcnt) > total_param) { diff -u --recursive --new-file v2.1.31/linux/fs/super.c linux/fs/super.c --- v2.1.31/linux/fs/super.c Fri Apr 4 08:52:24 1997 +++ linux/fs/super.c Fri Apr 4 11:24:18 1997 @@ -325,6 +325,7 @@ len += sprintf(buf+len, ",wsize=%d", nfss->wsize); } +#if 0 if (nfss->timeo != 7*HZ/10) { len += sprintf(buf+len, ",timeo=%d", nfss->timeo*10/HZ); @@ -333,6 +334,7 @@ len += sprintf(buf+len, ",retrans=%d", nfss->retrans); } +#endif if (nfss->acregmin != 3*HZ) { len += sprintf(buf+len, ",acregmin=%d", nfss->acregmin/HZ); diff -u --recursive --new-file v2.1.31/linux/include/asm-alpha/ptrace.h linux/include/asm-alpha/ptrace.h --- v2.1.31/linux/include/asm-alpha/ptrace.h Thu Dec 21 22:22:06 1995 +++ linux/include/asm-alpha/ptrace.h Thu Apr 3 09:56:30 1997 @@ -37,6 +37,10 @@ unsigned long r27; unsigned long r28; unsigned long hae; +/* JRP - These are the values provided to a0-a2 by PALcode */ + unsigned long trap_a0; + unsigned long trap_a1; + unsigned long trap_a2; /* These are saved by PAL-code: */ unsigned long ps; unsigned long pc; diff -u --recursive --new-file v2.1.31/linux/include/asm-i386/bitops.h linux/include/asm-i386/bitops.h --- v2.1.31/linux/include/asm-i386/bitops.h Thu Mar 27 14:40:06 1997 +++ linux/include/asm-i386/bitops.h Fri Apr 4 10:49:42 1997 @@ -30,7 +30,7 @@ { int oldbit; - __asm__ __volatile__(LOCK_PREFIX + __asm__ __volatile__( LOCK_PREFIX "btsl %2,%1\n\tsbbl %0,%0" :"=r" (oldbit),"=m" (ADDR) :"ir" (nr)); @@ -41,7 +41,7 @@ { int oldbit; - __asm__ __volatile__(LOCK_PREFIX + __asm__ __volatile__( LOCK_PREFIX "btrl %2,%1\n\tsbbl %0,%0" :"=r" (oldbit),"=m" (ADDR) :"ir" (nr)); @@ -52,7 +52,7 @@ { int oldbit; - __asm__ __volatile__(LOCK_PREFIX + __asm__ __volatile__( LOCK_PREFIX "btcl %2,%1\n\tsbbl %0,%0" :"=r" (oldbit),"=m" (ADDR) :"ir" (nr)); diff -u --recursive --new-file v2.1.31/linux/include/asm-i386/hardirq.h linux/include/asm-i386/hardirq.h --- v2.1.31/linux/include/asm-i386/hardirq.h Thu Mar 27 14:40:06 1997 +++ linux/include/asm-i386/hardirq.h Thu Apr 3 16:31:02 1997 @@ -12,6 +12,8 @@ #define hardirq_enter(cpu) (local_irq_count[cpu]++) #define hardirq_exit(cpu) (local_irq_count[cpu]--) +#define synchronize_irq() do { } while (0) + #else extern unsigned char global_irq_holder; @@ -61,7 +63,9 @@ __cli(); hardirq_exit(cpu); __sti(); -} +} + +extern void synchronize_irq(void); #endif /* __SMP__ */ diff -u --recursive --new-file v2.1.31/linux/include/asm-i386/system.h linux/include/asm-i386/system.h --- v2.1.31/linux/include/asm-i386/system.h Thu Mar 27 14:40:06 1997 +++ linux/include/asm-i386/system.h Fri Apr 4 08:39:01 1997 @@ -187,6 +187,9 @@ struct __xchg_dummy { unsigned long a[100]; }; #define __xg(x) ((struct __xchg_dummy *)(x)) +/* + * Note: no "lock" prefix even on SMP: xchg always implies lock anyway + */ static inline unsigned long __xchg(unsigned long x, void * ptr, int size) { switch (size) { diff -u --recursive --new-file v2.1.31/linux/include/linux/fs.h linux/include/linux/fs.h --- v2.1.31/linux/include/linux/fs.h Fri Apr 4 08:52:25 1997 +++ linux/include/linux/fs.h Fri Apr 4 12:15:40 1997 @@ -667,6 +667,7 @@ __bforget(buf); } extern void set_blocksize(kdev_t dev, int size); +extern unsigned int get_hardblocksize(kdev_t dev); extern struct buffer_head * bread(kdev_t dev, int block, int size); extern struct buffer_head * breada(kdev_t dev,int block, int size, unsigned int pos, unsigned int filesize); diff -u --recursive --new-file v2.1.31/linux/include/linux/interrupt.h linux/include/linux/interrupt.h --- v2.1.31/linux/include/linux/interrupt.h Fri Apr 4 08:52:25 1997 +++ linux/include/linux/interrupt.h Fri Apr 4 11:16:00 1997 @@ -80,9 +80,8 @@ extern inline void start_bh_atomic(void) { #ifdef __SMP__ - cli(); atomic_inc(&intr_count); - sti(); + synchronize_irq(); #else intr_count++; barrier(); diff -u --recursive --new-file v2.1.31/linux/include/linux/lockd/bind.h linux/include/linux/lockd/bind.h --- v2.1.31/linux/include/linux/lockd/bind.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/lockd/bind.h Fri Dec 13 08:25:50 1996 @@ -0,0 +1,43 @@ +/* + * linux/include/linux/lockd/bind.h + * + * This is the part of lockd visible to nfsd and the nfs client. + * + * Copyright (C) 1996, Olaf Kirch + */ + +#ifndef LINUX_LOCKD_BIND_H +#define LINUX_LOCKD_BIND_H + +#include + +/* Dummy declarations */ +struct svc_rqst; +struct knfs_fh; +struct svc_client; /* opaque type */ + +/* + * This is the set of functions for lockd->nfsd communication + */ +struct nlmsvc_binding { + void (*exp_readlock)(void); + void (*exp_unlock)(void); + struct svc_client * (*exp_getclient)(struct sockaddr_in *); + u32 (*fopen)(struct svc_rqst *, + struct knfs_fh *, + struct file *); + void (*fclose)(struct file *); + void (*detach)(void); +}; + +extern struct nlmsvc_binding * nlmsvc_ops; + +/* + * Functions exported by the lockd module + */ +extern void nlmsvc_invalidate_client(struct svc_client *clnt); +extern int nlmclnt_proc(struct inode *, int, struct file_lock *); +extern int lockd_up(void); +extern void lockd_down(void); + +#endif /* LINUX_LOCKD_BIND_H */ diff -u --recursive --new-file v2.1.31/linux/include/linux/lockd/debug.h linux/include/linux/lockd/debug.h --- v2.1.31/linux/include/linux/lockd/debug.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/lockd/debug.h Fri Apr 4 12:19:14 1997 @@ -0,0 +1,51 @@ +/* + * linux/include/linux/lockd/debug.h + * + * Debugging stuff. + * + * Copyright (C) 1996 Olaf Kirch + */ + +#ifndef LINUX_LOCKD_DEBUG_H +#define LINUX_LOCKD_DEBUG_H + +#ifdef __KERNEL__ + +#include + +/* + * Enable lockd debugging. + * Requires RPC_DEBUG. + */ +#ifdef RPC_DEBUG +# define LOCKD_DEBUG 1 +#endif + +#undef ifdebug +#if defined(RPC_DEBUG) && defined(LOCKD_DEBUG) +# define ifdebug(flag) if (nlm_debug & NLMDBG_##flag) +#else +# define ifdebug(flag) if (0) +#endif + +#ifdef LOCKD_DEBUG +char * nlm_procname(u32); +#endif + +#endif /* __KERNEL__ */ + +/* + * Debug flags + */ +#define NLMDBG_SVC 0x0001 +#define NLMDBG_CLIENT 0x0002 +#define NLMDBG_CLNTLOCK 0x0004 +#define NLMDBG_SVCLOCK 0x0008 +#define NLMDBG_MONITOR 0x0010 +#define NLMDBG_CLNTSUBS 0x0020 +#define NLMDBG_SVCSUBS 0x0040 +#define NLMDBG_HOSTCACHE 0x0080 +#define NLMDBG_ALL 0x7fff + + +#endif /* LINUX_LOCKD_DEBUG_H */ diff -u --recursive --new-file v2.1.31/linux/include/linux/lockd/lockd.h linux/include/linux/lockd/lockd.h --- v2.1.31/linux/include/linux/lockd/lockd.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/lockd/lockd.h Fri Apr 4 12:19:14 1997 @@ -0,0 +1,196 @@ +/* + * linux/include/linux/lockd/lockd.h + * + * General-purpose lockd include file. + * + * Copyright (C) 1996 Olaf Kirch + */ + +#ifndef LINUX_LOCKD_LOCKD_H +#define LINUX_LOCKD_LOCKD_H + +#ifdef __KERNEL__ + +#include +#include +#include +#include +#include +#include +#include + +/* + * Version string + */ +#define LOCKD_VERSION "0.4" + +/* + * Default timeout for RPC calls (seconds) + */ +#define LOCKD_DFLT_TIMEO 10 + +/* + * Lockd host handle (used both by the client and server personality). + */ +struct nlm_host { + struct nlm_host * h_next; /* linked list (hash table) */ + struct sockaddr_in h_addr; /* peer address */ + struct svc_client * h_exportent; /* NFS client */ + struct rpc_clnt * h_rpcclnt; /* RPC client to talk to peer */ + char h_name[20]; /* remote hostname */ + u32 h_version; /* interface version */ + unsigned short h_proto; /* transport proto */ + unsigned short h_authflavor; /* RPC authentication type */ + unsigned short h_reclaiming : 1, + h_inuse : 1, + h_monitored : 1; + struct wait_queue * h_gracewait; /* wait while reclaiming */ + u32 h_state; /* pseudo-state counter */ + u32 h_nsmstate; /* true remote NSM state */ + unsigned int h_count; /* reference count */ + struct semaphore h_sema; /* mutex for pmap binding */ + unsigned long h_nextrebind; /* next portmap call */ + unsigned long h_expires; /* eligible for GC */ +}; + +/* + * Memory chunk for NLM client RPC request. + */ +#define NLMCLNT_OHSIZE (sizeof(system_utsname.nodename)+10) +struct nlm_rqst { + unsigned int a_flags; /* initial RPC task flags */ + struct nlm_host * a_host; /* host handle */ + struct nlm_args a_args; /* arguments */ + struct nlm_res a_res; /* result */ + char a_owner[NLMCLNT_OHSIZE]; +}; + +/* + * This struct describes a file held open by lockd on behalf of + * an NFS client. + */ +struct nlm_file { + struct nlm_file * f_next; /* linked list */ + struct knfs_fh f_handle; /* NFS file handle */ + struct file f_file; /* VFS file pointer */ + struct nlm_share * f_shares; /* DOS shares */ + struct nlm_block * f_blocks; /* blocked locks */ + unsigned int f_locks; /* guesstimate # of locks */ + unsigned int f_count; /* reference count */ + struct semaphore f_sema; /* avoid concurrent access */ +}; + +/* + * This is a server block (i.e. a lock requested by some client which + * couldn't be granted because of a conflicting lock). + */ +#define NLM_NEVER (~(unsigned long) 0) +struct nlm_block { + struct nlm_block * b_next; /* linked list (all blocks) */ + struct nlm_block * b_fnext; /* linked list (per file) */ + struct nlm_rqst b_call; /* RPC args & callback info */ + struct svc_serv * b_daemon; /* NLM service */ + struct nlm_host * b_host; /* host handle for RPC clnt */ + unsigned long b_when; /* next re-xmit */ + unsigned int b_id; /* block id */ + unsigned char b_queued; /* re-queued */ + unsigned char b_granted; /* VFS granted lock */ + unsigned char b_incall; /* doing callback */ + unsigned char b_done; /* callback complete */ + struct nlm_file * b_file; /* file in question */ +}; + +/* + * Valid actions for nlmsvc_traverse_files + */ +#define NLM_ACT_CHECK 0 /* check for locks */ +#define NLM_ACT_MARK 1 /* mark & sweep */ +#define NLM_ACT_UNLOCK 2 /* release all locks */ + +/* + * Global variables + */ +extern struct rpc_program nlm_program; +extern struct svc_procedure nlmsvc_procedures[]; +extern unsigned long nlmsvc_grace_period; +extern unsigned long nlmsvc_timeout; + +/* + * Lockd client functions + */ +struct nlm_rqst * nlmclnt_alloc_call(void); +int nlmclnt_call(struct nlm_rqst *, u32); +int nlmclnt_async_call(struct nlm_rqst *, u32, rpc_action); +int nlmclnt_block(struct nlm_host *, struct file_lock *, u32 *); +int nlmclnt_cancel(struct nlm_host *, struct file_lock *); +u32 nlmclnt_grant(struct nlm_lock *); +void nlmclnt_recovery(struct nlm_host *, u32); +int nlmclnt_reclaim(struct nlm_host *, struct file_lock *); +int nlmclnt_setgrantargs(struct nlm_rqst *, struct nlm_lock *); +void nlmclnt_freegrantargs(struct nlm_rqst *); + +/* + * Host cache + */ +struct nlm_host * nlmclnt_lookup_host(struct sockaddr_in *, int, int); +struct nlm_host * nlmsvc_lookup_host(struct svc_rqst *); +struct nlm_host * nlm_lookup_host(struct svc_client *, + struct sockaddr_in *, int, int); +struct rpc_clnt * nlm_bind_host(struct nlm_host *); +void nlm_rebind_host(struct nlm_host *); +void nlm_release_host(struct nlm_host *); +void nlm_shutdown_hosts(void); + +/* + * Server-side lock handling + */ +u32 nlmsvc_lock(struct svc_rqst *, struct nlm_file *, + struct nlm_lock *, int, u32); +u32 nlmsvc_unlock(struct nlm_file *, struct nlm_lock *); +u32 nlmsvc_testlock(struct nlm_file *, struct nlm_lock *, + struct nlm_lock *); +u32 nlmsvc_cancel_blocked(struct nlm_file *, struct nlm_lock *); +unsigned long nlmsvc_retry_blocked(void); +int nlmsvc_traverse_blocks(struct nlm_host *, struct nlm_file *, + int action); + +/* + * File handling for the server personality + */ +u32 nlm_lookup_file(struct svc_rqst *, struct nlm_file **, + struct nfs_fh *); +void nlm_release_file(struct nlm_file *); +void nlmsvc_mark_resources(void); +void nlmsvc_free_host_resources(struct nlm_host *); + +extern __inline__ struct inode * +nlmsvc_file_inode(struct nlm_file *file) +{ + return file->f_file.f_inode; +} + +/* + * Compare two host addresses (needs modifying for ipv6) + */ +extern __inline__ int +nlm_cmp_addr(struct sockaddr_in *sin1, struct sockaddr_in *sin2) +{ + return sin1->sin_addr.s_addr == sin2->sin_addr.s_addr; +} + +/* + * Compare two NLM locks. + * When the second lock is of type F_UNLCK, this acts like a wildcard. + */ +extern __inline__ int +nlm_compare_locks(struct file_lock *fl1, struct file_lock *fl2) +{ + return fl1->fl_pid == fl2->fl_pid + && fl1->fl_start == fl2->fl_start + && fl1->fl_end == fl2->fl_end + &&(fl1->fl_type == fl2->fl_type || fl2->fl_type == F_UNLCK); +} + +#endif /* __KERNEL__ */ + +#endif /* LINUX_LOCKD_LOCKD_H */ diff -u --recursive --new-file v2.1.31/linux/include/linux/lockd/nlm.h linux/include/linux/lockd/nlm.h --- v2.1.31/linux/include/linux/lockd/nlm.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/lockd/nlm.h Fri Dec 13 08:23:14 1996 @@ -0,0 +1,52 @@ +/* + * linux/include/linux/lockd/nlm.h + * + * Declarations for the Network Lock Manager protocol. + * + * Copyright (C) 1996, Olaf Kirch + */ + +#ifndef LINUX_LOCKD_NLM_H +#define LINUX_LOCKD_NLM_H + +/* Maximum file offset in file_lock.fl_end */ +#ifdef OFFSET_MAX +# define NLM_OFFSET_MAX OFFSET_MAX +#else +# define NLM_OFFSET_MAX ((off_t) 0x7fffffff) +#endif + +/* Return states for NLM */ +enum { + NLM_LCK_GRANTED = 0, + NLM_LCK_DENIED, + NLM_LCK_DENIED_NOLOCKS, + NLM_LCK_BLOCKED, + NLM_LCK_DENIED_GRACE_PERIOD, +}; + +#define NLM_PROGRAM 100021 + +#define NLMPROC_NULL 0 +#define NLMPROC_TEST 1 +#define NLMPROC_LOCK 2 +#define NLMPROC_CANCEL 3 +#define NLMPROC_UNLOCK 4 +#define NLMPROC_GRANTED 5 +#define NLMPROC_TEST_MSG 6 +#define NLMPROC_LOCK_MSG 7 +#define NLMPROC_CANCEL_MSG 8 +#define NLMPROC_UNLOCK_MSG 9 +#define NLMPROC_GRANTED_MSG 10 +#define NLMPROC_TEST_RES 11 +#define NLMPROC_LOCK_RES 12 +#define NLMPROC_CANCEL_RES 13 +#define NLMPROC_UNLOCK_RES 14 +#define NLMPROC_GRANTED_RES 15 +#define NLMPROC_SHARE 20 +#define NLMPROC_UNSHARE 21 +#define NLMPROC_NM_LOCK 22 +#define NLMPROC_FREE_ALL 23 +#define NLMPROC_NSM_NOTIFY 24 /* statd callback */ + +#endif /* LINUX_LOCKD_NLM_H */ diff -u --recursive --new-file v2.1.31/linux/include/linux/lockd/share.h linux/include/linux/lockd/share.h --- v2.1.31/linux/include/linux/lockd/share.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/lockd/share.h Thu Dec 12 14:22:19 1996 @@ -0,0 +1,30 @@ +/* + * linux/include/linux/lockd/share.h + * + * DOS share management for lockd. + * + * Copyright (C) 1996, Olaf Kirch + */ + +#ifndef LINUX_LOCKD_SHARE_H +#define LINUX_LOCKD_SHARE_H + +/* + * DOS share for a specific file + */ +struct nlm_share { + struct nlm_share * s_next; /* linked list */ + struct nlm_host * s_host; /* client host */ + struct nlm_file * s_file; /* shared file */ + struct xdr_netobj s_owner; /* owner handle */ + u32 s_access; /* access mode */ + u32 s_mode; /* deny mode */ +}; + +u32 nlmsvc_share_file(struct nlm_host *, struct nlm_file *, + struct nlm_args *); +u32 nlmsvc_unshare_file(struct nlm_host *, struct nlm_file *, + struct nlm_args *); +int nlmsvc_traverse_shares(struct nlm_host *, struct nlm_file *, int); + +#endif /* LINUX_LOCKD_SHARE_H */ diff -u --recursive --new-file v2.1.31/linux/include/linux/lockd/sm_inter.h linux/include/linux/lockd/sm_inter.h --- v2.1.31/linux/include/linux/lockd/sm_inter.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/lockd/sm_inter.h Wed Dec 4 02:54:18 1996 @@ -0,0 +1,45 @@ +/* + * linux/include/linux/lockd/sm_inter.h + * + * Declarations for the kernel statd client. + * + * Copyright (C) 1996, Olaf Kirch + */ + +#ifndef LINUX_LOCKD_SM_INTER_H +#define LINUX_LOCKD_SM_INTER_H + +#define SM_PROGRAM 100024 +#define SM_VERSION 1 +#define SM_STAT 1 +#define SM_MON 2 +#define SM_UNMON 3 +#define SM_UNMON_ALL 4 +#define SM_SIMU_CRASH 5 +#define SM_NOTIFY 6 + +#define SM_MAXSTRLEN 1024 + +/* + * Arguments for all calls to statd + */ +struct nsm_args { + u32 addr; /* remote address */ + u32 prog; /* RPC callback info */ + u32 vers; + u32 proc; +}; + +/* + * Result returned by statd + */ +struct nsm_res { + u32 status; + u32 state; +}; + +int nsm_monitor(struct nlm_host *); +int nsm_unmonitor(struct nlm_host *); +extern u32 nsm_local_state; + +#endif /* LINUX_LOCKD_SM_INTER_H */ diff -u --recursive --new-file v2.1.31/linux/include/linux/lockd/xdr.h linux/include/linux/lockd/xdr.h --- v2.1.31/linux/include/linux/lockd/xdr.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/lockd/xdr.h Fri Apr 4 12:19:14 1997 @@ -0,0 +1,86 @@ +/* + * linux/include/linux/lockd/xdr.h + * + * XDR types for the NLM protocol + * + * Copyright (C) 1996 Olaf Kirch + */ + +#ifndef LOCKD_XDR_H +#define LOCKD_XDR_H + +#include +#include +#include + +extern u32 nlm_granted, nlm_lck_denied, nlm_lck_denied_nolocks, + nlm_lck_blocked, nlm_lck_denied_grace_period; + +/* Lock info passed via NLM */ +struct nlm_lock { + char * caller; + struct nfs_fh fh; + struct xdr_netobj oh; + struct file_lock fl; +}; + +/* + * Generic lockd arguments for all but sm_notify + */ +struct nlm_args { + u32 cookie; + struct nlm_lock lock; + u32 block; + u32 reclaim; + u32 state; + u32 monitor; + u32 fsm_access; + u32 fsm_mode; +}; + +/* + * Generic lockd result + */ +struct nlm_res { + u32 cookie; + u32 status; + struct nlm_lock lock; +}; + +/* + * statd callback when client has rebooted + */ +struct nlm_reboot { + char * mon; + int len; + u32 state; + u32 addr; +}; + +/* + * Contents of statd callback when monitored host rebooted + */ +#define NLMSVC_XDRSIZE sizeof(struct nlm_args) + +void nlmxdr_init(void); +int nlmsvc_decode_testargs(struct svc_rqst *, u32 *, struct nlm_args *); +int nlmsvc_encode_testres(struct svc_rqst *, u32 *, struct nlm_res *); +int nlmsvc_decode_lockargs(struct svc_rqst *, u32 *, struct nlm_args *); +int nlmsvc_decode_cancargs(struct svc_rqst *, u32 *, struct nlm_args *); +int nlmsvc_decode_unlockargs(struct svc_rqst *, u32 *, struct nlm_args *); +int nlmsvc_encode_res(struct svc_rqst *, u32 *, struct nlm_res *); +int nlmsvc_decode_res(struct svc_rqst *, u32 *, struct nlm_res *); +int nlmsvc_encode_void(struct svc_rqst *, u32 *, void *); +int nlmsvc_decode_void(struct svc_rqst *, u32 *, void *); +int nlmsvc_decode_shareargs(struct svc_rqst *, u32 *, struct nlm_args *); +int nlmsvc_encode_shareres(struct svc_rqst *, u32 *, struct nlm_res *); +int nlmsvc_decode_notify(struct svc_rqst *, u32 *, struct nlm_args *); +int nlmsvc_decode_reboot(struct svc_rqst *, u32 *, struct nlm_reboot *); +/* +int nlmclt_encode_testargs(struct rpc_rqst *, u32 *, struct nlm_args *); +int nlmclt_encode_lockargs(struct rpc_rqst *, u32 *, struct nlm_args *); +int nlmclt_encode_cancargs(struct rpc_rqst *, u32 *, struct nlm_args *); +int nlmclt_encode_unlockargs(struct rpc_rqst *, u32 *, struct nlm_args *); + */ + +#endif /* LOCKD_XDR_H */ diff -u --recursive --new-file v2.1.31/linux/include/linux/nfs.h linux/include/linux/nfs.h --- v2.1.31/linux/include/linux/nfs.h Sun Jan 29 20:51:02 1995 +++ linux/include/linux/nfs.h Mon Mar 3 01:14:03 1997 @@ -1,65 +1,28 @@ +/* + * NFS protocol definitions + */ #ifndef _LINUX_NFS_H #define _LINUX_NFS_H -#define NFS_PORT 2049 -#define NFS_MAXDATA 8192 -#define NFS_MAXPATHLEN 1024 -#define NFS_MAXNAMLEN 255 -#define NFS_MAXGROUPS 16 -#define NFS_FHSIZE 32 -#define NFS_COOKIESIZE 4 -#define NFS_FIFO_DEV (-1) -#define NFSMODE_FMT 0170000 -#define NFSMODE_DIR 0040000 -#define NFSMODE_CHR 0020000 -#define NFSMODE_BLK 0060000 -#define NFSMODE_REG 0100000 -#define NFSMODE_LNK 0120000 -#define NFSMODE_SOCK 0140000 -#define NFSMODE_FIFO 0010000 - -#ifdef __KERNEL__ /* user programs should get these from the rpc header files */ - -#define RPC_VERSION 2 - -enum rpc_auth_flavor { - RPC_AUTH_NULL = 0, - RPC_AUTH_UNIX = 1, - RPC_AUTH_SHORT = 2 -}; - -enum rpc_msg_type { - RPC_CALL = 0, - RPC_REPLY = 1 -}; - -enum rpc_reply_stat { - RPC_MSG_ACCEPTED = 0, - RPC_MSG_DENIED = 1 -}; - -enum rpc_accept_stat { - RPC_SUCCESS = 0, - RPC_PROG_UNAVAIL = 1, - RPC_PROG_MISMATCH = 2, - RPC_PROC_UNAVAIL = 3, - RPC_GARBAGE_ARGS = 4 -}; - -enum rpc_reject_stat { - RPC_MISMATCH = 0, - RPC_AUTH_ERROR = 1 -}; - -enum rpc_auth_stat { - RPC_AUTH_BADCRED = 1, - RPC_AUTH_REJECTEDCRED = 2, - RPC_AUTH_BADVERF = 3, - RPC_AUTH_REJECTEDVERF = 4, - RPC_AUTH_TOOWEAK = 5 -}; +#include + +#define NFS_PORT 2049 +#define NFS_MAXDATA 8192 +#define NFS_MAXPATHLEN 1024 +#define NFS_MAXNAMLEN 255 +#define NFS_MAXGROUPS 16 +#define NFS_FHSIZE 32 +#define NFS_COOKIESIZE 4 +#define NFS_FIFO_DEV (-1) +#define NFSMODE_FMT 0170000 +#define NFSMODE_DIR 0040000 +#define NFSMODE_CHR 0020000 +#define NFSMODE_BLK 0060000 +#define NFSMODE_REG 0100000 +#define NFSMODE_LNK 0120000 +#define NFSMODE_SOCK 0140000 +#define NFSMODE_FIFO 0010000 -#endif /* __KERNEL__ */ enum nfs_stat { NFS_OK = 0, @@ -96,6 +59,10 @@ NFFIFO = 8 }; +struct nfs_fh { + char data[NFS_FHSIZE]; +}; + #define NFS_PROGRAM 100003 #define NFS_VERSION 2 #define NFSPROC_NULL 0 @@ -117,54 +84,140 @@ #define NFSPROC_READDIR 16 #define NFSPROC_STATFS 17 -struct nfs_fh { - char data[NFS_FHSIZE]; -}; +#if defined(__KERNEL__) || defined(NFS_NEED_KERNEL_TYPES) + +extern struct rpc_program nfs_program; +extern struct rpc_stat nfs_rpcstat; struct nfs_time { - u_int seconds; - u_int useconds; + __u32 seconds; + __u32 useconds; }; struct nfs_fattr { - enum nfs_ftype type; - u_int mode; - u_int nlink; - u_int uid; - u_int gid; - u_int size; - u_int blocksize; - u_int rdev; - u_int blocks; - u_int fsid; - u_int fileid; - struct nfs_time atime; - struct nfs_time mtime; - struct nfs_time ctime; + enum nfs_ftype type; + __u32 mode; + __u32 nlink; + __u32 uid; + __u32 gid; + __u32 size; + __u32 blocksize; + __u32 rdev; + __u32 blocks; + __u32 fsid; + __u32 fileid; + struct nfs_time atime; + struct nfs_time mtime; + struct nfs_time ctime; }; struct nfs_sattr { - u_int mode; - u_int uid; - u_int gid; - u_int size; - struct nfs_time atime; - struct nfs_time mtime; + __u32 mode; + __u32 uid; + __u32 gid; + __u32 size; + struct nfs_time atime; + struct nfs_time mtime; }; struct nfs_entry { - u_int fileid; - char *name; - int cookie; - int eof; + __u32 fileid; + char * name; + unsigned int length; + __u32 cookie; + __u32 eof; }; struct nfs_fsinfo { - u_int tsize; - u_int bsize; - u_int blocks; - u_int bfree; - u_int bavail; + __u32 tsize; + __u32 bsize; + __u32 blocks; + __u32 bfree; + __u32 bavail; +}; + +#ifdef NFS_NEED_XDR_TYPES + +struct nfs_sattrargs { + struct nfs_fh * fh; + struct nfs_sattr * sattr; +}; + +struct nfs_diropargs { + struct nfs_fh * fh; + const char * name; +}; + +struct nfs_readargs { + struct nfs_fh * fh; + __u32 offset; + __u32 count; + void * buffer; +}; + +struct nfs_writeargs { + struct nfs_fh * fh; + __u32 offset; + __u32 count; + const void * buffer; +}; + +struct nfs_createargs { + struct nfs_fh * fh; + const char * name; + struct nfs_sattr * sattr; +}; + +struct nfs_renameargs { + struct nfs_fh * fromfh; + const char * fromname; + struct nfs_fh * tofh; + const char * toname; +}; + +struct nfs_linkargs { + struct nfs_fh * fromfh; + struct nfs_fh * tofh; + const char * toname; }; + +struct nfs_symlinkargs { + struct nfs_fh * fromfh; + const char * fromname; + const char * topath; + struct nfs_sattr * sattr; +}; + +struct nfs_readdirargs { + struct nfs_fh * fh; + __u32 cookie; + void * buffer; + unsigned int bufsiz; +}; + +struct nfs_diropok { + struct nfs_fh * fh; + struct nfs_fattr * fattr; +}; + +struct nfs_readres { + struct nfs_fattr * fattr; + unsigned int count; +}; + +struct nfs_readlinkres { + char ** string; + unsigned int * lenp; + unsigned int maxlen; + void * buffer; +}; + +struct nfs_readdirres { + void * buffer; + unsigned int bufsiz; +}; + +#endif /* NFS_NEED_XDR_TYPES */ +#endif /* __KERNEL__ */ #endif diff -u --recursive --new-file v2.1.31/linux/include/linux/nfs3.h linux/include/linux/nfs3.h --- v2.1.31/linux/include/linux/nfs3.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/nfs3.h Thu Dec 12 16:02:54 1996 @@ -0,0 +1,252 @@ +/* + * NFSv3 protocol definitions + */ +#ifndef _LINUX_NFS3_H +#define _LINUX_NFS3_H + +#include +#include + +#define NFS3_PORT 2049 +#define NFS3_MAXDATA 8192 +#define NFS3_MAXPATHLEN PATH_MAX +#define NFS3_MAXNAMLEN NAME_MAX +#define NFS3_MAXGROUPS 16 +#define NFS3_FHSIZE NFS_FHSIZE +#define NFS3_COOKIESIZE 4 +#define NFS3_FIFO_DEV (-1) +#define NFS3MODE_FMT 0170000 +#define NFS3MODE_DIR 0040000 +#define NFS3MODE_CHR 0020000 +#define NFS3MODE_BLK 0060000 +#define NFS3MODE_REG 0100000 +#define NFS3MODE_LNK 0120000 +#define NFS3MODE_SOCK 0140000 +#define NFS3MODE_FIFO 0010000 + + +enum nfs3_stat { + NFS3_OK = 0, + NFS3ERR_PERM = 1, + NFS3ERR_NOENT = 2, + NFS3ERR_IO = 5, + NFS3ERR_NXIO = 6, + NFS3ERR_EAGAIN = 11, + NFS3ERR_ACCES = 13, + NFS3ERR_EXIST = 17, + NFS3ERR_XDEV = 18, /* new in NFSv3 */ + NFS3ERR_NODEV = 19, + NFS3ERR_NOTDIR = 20, + NFS3ERR_ISDIR = 21, + NFS3ERR_INVAL = 22, /* new in NFSv3 */ + NFS3ERR_FBIG = 27, + NFS3ERR_NOSPC = 28, + NFS3ERR_ROFS = 30, + NFS3ERR_MLINK = 31, /* new in NFSv3 */ + NFS3ERR_NAMETOOLONG = 63, + NFS3ERR_NOTEMPTY = 66, + NFS3ERR_DQUOT = 69, + NFS3ERR_STALE = 70, + NFS3ERR_REMOTE = 71, /* new in NFSv3 */ + NFS3ERR_BADHANDLE = 10001,/* ditto */ + NFS3ERR_NOT_SYNC = 10002,/* ditto */ + NFS3ERR_BAD_COOKIE = 10003,/* ditto */ + NFS3ERR_NOTSUPP = 10004,/* ditto */ + NFS3ERR_TOOSMALL = 10005,/* ditto */ + NFS3ERR_SERVERFAULT = 10006,/* ditto */ + NFS3ERR_BADTYPE = 10007,/* ditto */ + NFS3ERR_JUKEBOX = 10008,/* ditto */ +}; + +enum nfs3_ftype { + NF3NON = 0, + NF3REG = 1, + NF3DIR = 2, + NF3BLK = 3, + NF3CHR = 4, + NF3LNK = 5, + NF3SOCK = 6, + NF3FIFO = 7, /* changed from NFSv2 (was 8) */ + NF3BAD = 8 +}; + +#define NFS3_VERSION 3 +#define NFSPROC_NULL 0 +#define NFSPROC_GETATTR 1 +#define NFSPROC_SETATTR 2 +#define NFSPROC_ROOT 3 +#define NFSPROC_LOOKUP 4 +#define NFSPROC_READLINK 5 +#define NFSPROC_READ 6 +#define NFSPROC_WRITECACHE 7 +#define NFSPROC_WRITE 8 +#define NFSPROC_CREATE 9 +#define NFSPROC_REMOVE 10 +#define NFSPROC_RENAME 11 +#define NFSPROC_LINK 12 +#define NFSPROC_SYMLINK 13 +#define NFSPROC_MKDIR 14 +#define NFSPROC_RMDIR 15 +#define NFSPROC_READDIR 16 +#define NFSPROC_STATFS 17 + +#if defined(__KERNEL__) || defined(NFS_NEED_KERNEL_TYPES) + +struct nfs3_fh { + __u32 size; + __u8 data[NFS3_FHSIZE]; +}; + +struct nfs3_fattr { + enum nfs3_ftype type; + __u32 mode; + __u32 nlink; + __u32 uid; + __u32 gid; + __u64 size; + __u64 used; + __u32 rdev_maj; + __u32 rdev_min; + __u32 fsid; + __u32 fileid; + struct nfs_time atime; + struct nfs_time mtime; + struct nfs_time ctime; +}; + +struct nfs3_wcc_attr { + __u64 size; + struct nfs_time mtime; + struct nfs_time ctime; +}; + +struct nfs3_wcc_data { + struct nfs3_wcc_attr before; + struct nfs3_wcc_attr after; +}; + +struct nfs3_sattr { + __u32 valid; + __u32 mode; + __u32 uid; + __u32 gid; + __u64 size; + struct nfs_time atime; + struct nfs_time mtime; +}; + +struct nfs3_entry { + __u32 fileid; + char * name; + unsigned int length; + __u32 cookie; + __u32 eof; +}; + +struct nfs3_fsinfo { + __u32 tsize; + __u32 bsize; + __u32 blocks; + __u32 bfree; + __u32 bavail; +}; + +#ifdef NFS_NEED_XDR_TYPES + +struct nfs3_sattrargs { + struct nfs_fh * fh; + struct nfs_sattr * sattr; +}; + +struct nfs3_diropargs { + struct nfs_fh * fh; + const char * name; +}; + +struct nfs3_readargs { + struct nfs_fh * fh; + __u32 offset; + __u32 count; + void * buffer; +}; + +struct nfs3_writeargs { + struct nfs_fh * fh; + __u32 offset; + __u32 count; + const void * buffer; +}; + +struct nfs3_createargs { + struct nfs_fh * fh; + const char * name; + struct nfs_sattr * sattr; +}; + +struct nfs3_renameargs { + struct nfs_fh * fromfh; + const char * fromname; + struct nfs_fh * tofh; + const char * toname; +}; + +struct nfs3_linkargs { + struct nfs_fh * fromfh; + struct nfs_fh * tofh; + const char * toname; +}; + +struct nfs3_symlinkargs { + struct nfs_fh * fromfh; + const char * fromname; + const char * topath; + struct nfs_sattr * sattr; +}; + +struct nfs3_readdirargs { + struct nfs_fh * fh; + __u32 cookie; + void * buffer; + unsigned int bufsiz; +}; + +struct nfs3_diropok { + struct nfs_fh * fh; + struct nfs_fattr * fattr; +}; + +struct nfs3_readres { + struct nfs_fattr * fattr; + unsigned int count; +}; + +struct nfs3_readlinkres { + char ** string; + unsigned int * lenp; + unsigned int maxlen; + void * buffer; +}; + +struct nfs3_readdirres { + void * buffer; + unsigned int bufsiz; +}; + +/* + * The following are for NFSv3 + */ +struct nfs3_fh { + __u32 size; + __u8 data[NFS3_FHSIZE] +}; + +struct nfs3_wcc_attr { + __u64 size; + struct nfs_time mtime; + struct nfs_time ctime; +}; + +#endif /* NFS_NEED_XDR_TYPES */ +#endif /* __KERNEL__ */ + +#endif diff -u --recursive --new-file v2.1.31/linux/include/linux/nfs_fs.h linux/include/linux/nfs_fs.h --- v2.1.31/linux/include/linux/nfs_fs.h Thu Feb 6 02:53:33 1997 +++ linux/include/linux/nfs_fs.h Fri Apr 4 12:15:40 1997 @@ -1,6 +1,3 @@ -#ifndef _LINUX_NFS_FS_H -#define _LINUX_NFS_FS_H - /* * linux/include/linux/nfs_fs.h * @@ -9,26 +6,39 @@ * OS-specific nfs filesystem definitions and declarations */ -#include +#ifndef _LINUX_NFS_FS_H +#define _LINUX_NFS_FS_H +#include +#include +#include #include +#include #include /* - * The readdir cache size controls how many directory entries are cached. - * Its size is limited by the number of nfs_entry structures that can fit - * in one page, currently, the limit is 256 when using 4KB pages. + * Enable debugging support for nfs client. + * Requires RPC_DEBUG. */ +#ifdef RPC_DEBUG +# define NFS_DEBUG +#endif -#define NFS_READDIR_CACHE_SIZE 64 +/* + * NFS_MAX_DIRCACHE controls the number of simultaneously cached + * directory chunks. Each chunk holds the list of nfs_entry's returned + * in a single readdir call in a memory region of size PAGE_SIZE. + * + * Note that at most server->rsize bytes of the cache memory are used. + */ +#define NFS_MAX_DIRCACHE 16 #define NFS_MAX_FILE_IO_BUFFER_SIZE 16384 -#define NFS_DEF_FILE_IO_BUFFER_SIZE 1024 +#define NFS_DEF_FILE_IO_BUFFER_SIZE 4096 /* * The upper limit on timeouts for the exponential backoff algorithm. */ - #define NFS_MAX_RPC_TIMEOUT (6*HZ) /* @@ -36,113 +46,168 @@ * It is better not to make this too large although the optimum * depends on a usage and environment. */ - #define NFS_LOOKUP_CACHE_SIZE 64 +/* + * superblock magic number for NFS + */ #define NFS_SUPER_MAGIC 0x6969 #define NFS_SERVER(inode) (&(inode)->i_sb->u.nfs_sb.s_server) +#define NFS_CLIENT(inode) (NFS_SERVER(inode)->client) +#define NFS_ADDR(inode) (RPC_PEERADDR(NFS_CLIENT(inode))) +#define NFS_CONGESTED(inode) (RPC_CONGESTED(NFS_CLIENT(inode))) #define NFS_FH(inode) (&(inode)->u.nfs_i.fhandle) -#define NFS_RENAMED_DIR(inode) ((inode)->u.nfs_i.silly_rename_dir) + #define NFS_READTIME(inode) ((inode)->u.nfs_i.read_cache_jiffies) #define NFS_OLDMTIME(inode) ((inode)->u.nfs_i.read_cache_mtime) -#define NFS_ATTRTIMEO(inode) ((inode)->u.nfs_i.attrtimeo) -#define NFS_MINATTRTIMEO(inode) (S_ISREG((inode)->i_mode)? \ - NFS_SERVER(inode)->acregmin : \ - NFS_SERVER(inode)->acdirmin) #define NFS_CACHEINV(inode) \ do { \ NFS_READTIME(inode) = jiffies - 1000000; \ NFS_OLDMTIME(inode) = 0; \ } while (0) +#define NFS_ATTRTIMEO(inode) ((inode)->u.nfs_i.attrtimeo) +#define NFS_MINATTRTIMEO(inode) \ + (S_ISDIR(inode->i_mode)? NFS_SERVER(inode)->acdirmin \ + : NFS_SERVER(inode)->acregmin) +#define NFS_MAXATTRTIMEO(inode) \ + (S_ISDIR(inode->i_mode)? NFS_SERVER(inode)->acdirmax \ + : NFS_SERVER(inode)->acregmax) +#define NFS_FLAGS(inode) ((inode)->u.nfs_i.flags) +#define NFS_REVALIDATING(inode) (NFS_FLAGS(inode) & NFS_INO_REVALIDATE) -#ifdef __KERNEL__ +#define NFS_RENAMED_DIR(inode) ((inode)->u.nfs_i.silly_inode) +#define NFS_WRITEBACK(inode) ((inode)->u.nfs_i.writeback) -/* linux/fs/nfs/proc.c */ +/* + * These are the default flags for swap requests + */ +#define NFS_RPC_SWAPFLAGS (RPC_TASK_SWAPPER|RPC_TASK_ROOTCREDS) +#ifdef __KERNEL__ + +/* + * linux/fs/nfs/proc.c + */ extern int nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fattr *fattr); + struct nfs_fattr *fattr); extern int nfs_proc_setattr(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_sattr *sattr, struct nfs_fattr *fattr); + struct nfs_sattr *sattr, struct nfs_fattr *fattr); extern int nfs_proc_lookup(struct nfs_server *server, struct nfs_fh *dir, - const char *name, struct nfs_fh *fhandle, - struct nfs_fattr *fattr); + const char *name, struct nfs_fh *fhandle, + struct nfs_fattr *fattr); extern int nfs_proc_readlink(struct nfs_server *server, struct nfs_fh *fhandle, - int **p0, char **string, unsigned int *len, + void **p0, char **string, unsigned int *len, unsigned int maxlen); extern int nfs_proc_read(struct nfs_server *server, struct nfs_fh *fhandle, - int offset, int count, char *data, - struct nfs_fattr *fattr); -extern int nfs_proc_write(struct inode * inode, int offset, - int count, const char *data, struct nfs_fattr *fattr); + int swap, unsigned long offset, unsigned int count, + void *buffer, struct nfs_fattr *fattr); +extern int nfs_proc_write(struct nfs_server *server, struct nfs_fh *fhandle, + int swap, unsigned long offset, unsigned int count, + const void *buffer, struct nfs_fattr *fattr); extern int nfs_proc_create(struct nfs_server *server, struct nfs_fh *dir, - const char *name, struct nfs_sattr *sattr, - struct nfs_fh *fhandle, struct nfs_fattr *fattr); + const char *name, struct nfs_sattr *sattr, + struct nfs_fh *fhandle, struct nfs_fattr *fattr); extern int nfs_proc_remove(struct nfs_server *server, struct nfs_fh *dir, - const char *name); + const char *name); extern int nfs_proc_rename(struct nfs_server *server, - struct nfs_fh *old_dir, const char *old_name, - struct nfs_fh *new_dir, const char *new_name, - int must_be_dir); + struct nfs_fh *old_dir, const char *old_name, + struct nfs_fh *new_dir, const char *new_name); extern int nfs_proc_link(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fh *dir, const char *name); + struct nfs_fh *dir, const char *name); extern int nfs_proc_symlink(struct nfs_server *server, struct nfs_fh *dir, - const char *name, const char *path, struct nfs_sattr *sattr); + const char *name, const char *path, + struct nfs_sattr *sattr); extern int nfs_proc_mkdir(struct nfs_server *server, struct nfs_fh *dir, - const char *name, struct nfs_sattr *sattr, - struct nfs_fh *fhandle, struct nfs_fattr *fattr); + const char *name, struct nfs_sattr *sattr, + struct nfs_fh *fhandle, struct nfs_fattr *fattr); extern int nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir, - const char *name); + const char *name); extern int nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle, - int cookie, int count, struct nfs_entry *entry); + u32 cookie, unsigned int size, struct nfs_entry *entry); extern int nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fsinfo *res); -extern int nfs_proc_read_request(struct rpc_ioreq *, struct nfs_server *, - struct nfs_fh *, unsigned long offset, - unsigned long count, __u32 *buf); -extern int nfs_proc_read_reply(struct rpc_ioreq *, struct nfs_fattr *); -extern int *rpc_header(int *p, int procedure, int program, int version, - int uid, int gid, int ngroup, gid_t *groups); -extern int *rpc_verify(int *p); - -/* linux/fs/nfs/sock.c */ - -extern int nfs_rpc_call(struct nfs_server *server, int *start, - int *end, int size); -extern int nfs_rpc_doio(struct nfs_server *server, struct rpc_ioreq *, - int async); + struct nfs_fsinfo *res); -/* linux/fs/nfs/inode.c */ +/* + * linux/fs/nfs/inode.c + */ extern struct super_block *nfs_read_super(struct super_block *sb, void *data,int); extern int init_nfs_fs(void); extern struct inode *nfs_fhget(struct super_block *sb, struct nfs_fh *fhandle, struct nfs_fattr *fattr); extern void nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr); +extern int nfs_revalidate(struct inode *); +extern int _nfs_revalidate_inode(struct nfs_server *, struct inode *); -/* linux/fs/nfs/file.c */ - +/* + * linux/fs/nfs/file.c + */ extern struct inode_operations nfs_file_inode_operations; -/* linux/fs/nfs/dir.c */ - +/* + * linux/fs/nfs/dir.c + */ extern struct inode_operations nfs_dir_inode_operations; extern void nfs_sillyrename_cleanup(struct inode *); -extern void nfs_kfree_cache(void); - -/* linux/fs/nfs/symlink.c */ +extern void nfs_free_dircache(void); +extern void nfs_invalidate_dircache(struct inode *); +/* + * linux/fs/nfs/symlink.c + */ extern struct inode_operations nfs_symlink_inode_operations; -/* linux/fs/nfs/mmap.c */ +/* + * linux/fs/nfs/mmap.c + */ +extern int nfs_mmap(struct inode *inode, struct file *file, + struct vm_area_struct * vma); -extern int nfs_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma); +/* + * linux/fs/nfs/locks.c + */ +extern int nfs_lock(struct inode *inode, struct file *file, + int cmd, struct file_lock *fl); -/* linux/fs/nfs/bio.c */ +/* + * linux/fs/nfs/write.c + */ +extern int nfs_writepage(struct inode *, struct page *); +extern int nfs_check_error(struct inode *); +extern int nfs_flush_dirty_pages(struct inode *, off_t, off_t); +extern int nfs_truncate_dirty_pages(struct inode *, unsigned long); +extern void nfs_invalidate_pages(struct inode *); +extern int nfs_updatepage(struct inode *, struct page *, const char *, + unsigned long, unsigned int, int); + +/* + * linux/fs/nfs/read.c + */ +extern int nfs_readpage(struct inode *, struct page *); +extern int nfs_readpage_sync(struct inode *, struct page *); -extern int nfs_readpage(struct inode *, struct page *); +/* + * inline functions + */ +static inline int +nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) +{ + if (jiffies - NFS_READTIME(inode) < NFS_ATTRTIMEO(inode)) + return 0; + return _nfs_revalidate_inode(server, inode); +} + +extern struct nfs_wreq * nfs_failed_requests; +static inline int +nfs_write_error(struct inode *inode) +{ + if (nfs_failed_requests == NULL) + return 0; + return nfs_check_error(inode); +} /* NFS root */ @@ -156,5 +221,26 @@ extern char nfs_root_addrs[]; #endif /* __KERNEL__ */ + +/* + * NFS debug flags + */ +#define NFSDBG_VFS 0x0001 +#define NFSDBG_DIRCACHE 0x0002 +#define NFSDBG_LOOKUPCACHE 0x0004 +#define NFSDBG_PAGECACHE 0x0008 +#define NFSDBG_PROC 0x0010 +#define NFSDBG_XDR 0x0020 +#define NFSDBG_FILE 0x0040 +#define NFSDBG_ALL 0xFFFF + +#ifdef __KERNEL__ +# undef ifdebug +# ifdef NFS_DEBUG +# define ifdebug(fac) if (nfs_debug & NFSDBG_##fac) +# else +# define ifdebug(fac) if (0) +# endif +#endif /* __KERNEL */ #endif diff -u --recursive --new-file v2.1.31/linux/include/linux/nfs_fs_i.h linux/include/linux/nfs_fs_i.h --- v2.1.31/linux/include/linux/nfs_fs_i.h Fri Apr 4 08:52:25 1997 +++ linux/include/linux/nfs_fs_i.h Fri Apr 4 11:15:59 1997 @@ -8,35 +8,75 @@ * nfs fs inode data in memory */ struct nfs_inode_info { - struct pipe_inode_info pipeinfo; - struct nfs_fh fhandle; + /* + * This is a place holder so named pipes on NFS filesystems + * work (more or less correctly). This must be first in the + * struct because the data is really accessed via inode->u.pipe_i. + */ + struct pipe_inode_info pipeinfo; + + /* + * The file handle + */ + struct nfs_fh fhandle; + + /* + * Various flags + */ + unsigned short flags; + /* * read_cache_jiffies is when we started read-caching this inode, * and read_cache_mtime is the mtime of the inode at that time. + * attrtimeo is for how long the cached information is assumed + * to be valid. A successful attribute revalidation doubles + * attrtimeo (up to acregmax/acdirmax), a failure resets it to + * acregmin/acdirmin. + * + * We need to revalidate the cached attrs for this inode if * - * We need to invalidate the cache for this inode if + * jiffies - read_cache_jiffies > attrtimeo + * + * and invalidate any cached data/flush out any dirty pages if + * we find that * - * jiffies - read_cache_jiffies > 30*HZ - * AND * mtime != read_cache_mtime */ - unsigned long read_cache_jiffies; - unsigned long read_cache_mtime; + unsigned long read_cache_jiffies; + unsigned long read_cache_mtime; + unsigned long attrtimeo; + /* * This is to support the clandestine rename on unlink. - * Instead of the directory inode, we might as well keep its - * NFS FH, but that requires a kmalloc. + * Instead of the directory inode, we might as well keep + * its NFS FH, but that requires a kmalloc. */ - struct inode *silly_rename_dir; + struct inode * silly_inode; + /* - * attrtimeo defines for how long the cached attributes are valid + * This is the list of dirty unwritten pages. + * NFSv3 will want to add a list for written but uncommitted + * pages. */ - unsigned long attrtimeo; + struct nfs_wreq * writeback; }; +/* + * Legal inode flag values + */ +#define NFS_INO_REVALIDATE 0x0001 /* revalidating attrs */ + +/* + * NFS lock info + */ struct nfs_lock_info { u32 state; - unsigned int flags; + u32 flags; }; + +/* + * Lock flag values + */ +#define NFS_LCK_GRANTED 0x0001 /* lock has been granted */ #endif diff -u --recursive --new-file v2.1.31/linux/include/linux/nfs_fs_sb.h linux/include/linux/nfs_fs_sb.h --- v2.1.31/linux/include/linux/nfs_fs_sb.h Thu Dec 12 07:12:17 1996 +++ linux/include/linux/nfs_fs_sb.h Fri Apr 4 12:15:40 1997 @@ -1,34 +1,32 @@ #ifndef _NFS_FS_SB #define _NFS_FS_SB -#include #include +#include +/* + * NFS client parameters + * Part of this is duplicated in rpc_clnt and is therefore obsolete. + */ struct nfs_server { - struct file *file; - struct rpc_sock *rsock; - struct sockaddr toaddr ; /* Added for change to NFS code to use sendto() 1995-06-02 JSP */ - int lock; - struct wait_queue *wait; - int flags; - int rsize; - int wsize; - int timeo; - int retrans; - int acregmin; - int acregmax; - int acdirmin; - int acdirmax; - char hostname[256]; + struct rpc_clnt * client; /* RPC client handle */ + int flags; /* various flags */ + int rsize; /* read size */ + int wsize; /* write size */ + unsigned int bsize; /* server block size */ + unsigned int acregmin; /* attr cache timeouts */ + unsigned int acregmax; + unsigned int acdirmin; + unsigned int acdirmax; + char hostname[256]; /* remote hostname */ }; /* * nfs super-block data in memory */ - struct nfs_sb_info { - struct nfs_server s_server; - struct nfs_fh s_root; + struct nfs_server s_server; + struct nfs_fh s_root; }; #endif diff -u --recursive --new-file v2.1.31/linux/include/linux/nfs_mount.h linux/include/linux/nfs_mount.h --- v2.1.31/linux/include/linux/nfs_mount.h Tue Nov 14 06:03:57 1995 +++ linux/include/linux/nfs_mount.h Sat Feb 22 06:52:10 1997 @@ -16,36 +16,25 @@ * mount-to-kernel version compatibility. Some of these aren't used yet * but here they are anyway. */ - -#define NFS_NFS_PROGRAM 100003 /* nfsd program number */ -#define NFS_NFS_VERSION 2 /* nfsd version */ -#define NFS_NFS_PORT 2049 /* portnumber on server for nfsd */ - -#define NFS_MOUNT_PROGRAM 100005 /* mountd program number */ -#define NFS_MOUNT_VERSION 1 /* mountd version */ -#define NFS_MOUNT_PROC 1 /* mount process id */ -#define NFS_MOUNT_PORT 627 /* portnumber on server for mountd */ - -#define NFS_PMAP_PROGRAM 100000 /* portmap program number */ -#define NFS_PMAP_VERSION 2 /* portmap version */ -#define NFS_PMAP_PROC 3 /* portmap getport id */ -#define NFS_PMAP_PORT 111 /* portnumber on server for portmap */ +#define NFS_MOUNT_VERSION 3 struct nfs_mount_data { - int version; /* 1 */ - int fd; /* 1 */ - struct nfs_fh root; /* 1 */ - int flags; /* 1 */ - int rsize; /* 1 */ - int wsize; /* 1 */ - int timeo; /* 1 */ - int retrans; /* 1 */ - int acregmin; /* 1 */ - int acregmax; /* 1 */ - int acdirmin; /* 1 */ - int acdirmax; /* 1 */ - struct sockaddr_in addr; /* 1 */ - char hostname[256]; /* 1 */ + int version; /* 1 */ + int fd; /* 1 */ + struct nfs_fh root; /* 1 */ + int flags; /* 1 */ + int rsize; /* 1 */ + int wsize; /* 1 */ + int timeo; /* 1 */ + int retrans; /* 1 */ + int acregmin; /* 1 */ + int acregmax; /* 1 */ + int acdirmin; /* 1 */ + int acdirmax; /* 1 */ + struct sockaddr_in addr; /* 1 */ + char hostname[256]; /* 1 */ + int namlen; /* 2 */ + unsigned int bsize; /* 3 */ }; /* bits in the flags field */ @@ -56,5 +45,9 @@ #define NFS_MOUNT_POSIX 0x0008 /* 1 */ #define NFS_MOUNT_NOCTO 0x0010 /* 1 */ #define NFS_MOUNT_NOAC 0x0020 /* 1 */ +#define NFS_MOUNT_TCP 0x0040 /* 2 */ +#define NFS_MOUNT_VER3 0x0080 /* 3 */ +#define NFS_MOUNT_KERBEROS 0x0100 /* 3 */ +#define NFS_MOUNT_NONLM 0x0200 /* 3 */ #endif diff -u --recursive --new-file v2.1.31/linux/include/linux/nfsd/auth.h linux/include/linux/nfsd/auth.h --- v2.1.31/linux/include/linux/nfsd/auth.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/nfsd/auth.h Mon Oct 28 06:51:25 1996 @@ -0,0 +1,45 @@ +/* + * include/linux/nfsd/auth.h + * + * nfsd-specific authentication stuff. + * uid/gid mapping not yet implemented. + * + * Copyright (C) 1995, 1996 Olaf Kirch + */ + +#ifndef LINUX_NFSD_AUTH_H +#define LINUX_NFSD_AUTH_H + +#ifdef __KERNEL__ + +#define nfsd_luid(rq, uid) ((u32)(uid)) +#define nfsd_lgid(rq, gid) ((u32)(gid)) +#define nfsd_ruid(rq, uid) ((u32)(uid)) +#define nfsd_rgid(rq, gid) ((u32)(gid)) + +/* + * Set the current process's fsuid/fsgid etc to those of the NFS + * client user + */ +void nfsd_setuser(struct svc_rqst *, struct svc_export *); + +#if 0 +/* + * These must match the actual size of uid_t and gid_t + */ +#define UGID_BITS (8 * sizeof(uid_t)) +#define UGID_SHIFT 8 +#define UGID_MASK ((1 << UGID_SHIFT) - 1) +#define UGID_NRENTRIES ((1 << (UGID_BITS - UGID_SHIFT)) + 1) +#define UGID_NONE ((unsigned short)-1) + +typedef struct svc_uidmap { + uid_t * um_ruid[UGID_NRENTRIES]; + uid_t * um_luid[UGID_NRENTRIES]; + gid_t * um_rgid[UGID_NRENTRIES]; + gid_t * um_lgid[UGID_NRENTRIES]; +} svc_uidmap; +#endif + +#endif /* __KERNEL__ */ +#endif /* LINUX_NFSD_AUTH_H */ diff -u --recursive --new-file v2.1.31/linux/include/linux/nfsd/cache.h linux/include/linux/nfsd/cache.h --- v2.1.31/linux/include/linux/nfsd/cache.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/nfsd/cache.h Sun Dec 15 05:44:27 1996 @@ -0,0 +1,79 @@ +/* + * include/linux/nfsd/cache.h + * + * Request reply cache. This was heavily inspired by the + * implementation in 4.3BSD/4.4BSD. + * + * Copyright (C) 1995, 1996 Olaf Kirch + */ + +#ifndef NFSCACHE_H +#define NFSCACHE_H + +#ifdef __KERNEL__ +#include + +/* + * Representation of a reply cache entry. The first two members *must* + * be hash_next and hash_prev. + */ +struct svc_cacherep { + struct svc_cacherep * c_hash_next; + struct svc_cacherep * c_hash_prev; + struct svc_cacherep * c_lru_next; + struct svc_cacherep * c_lru_prev; + unsigned char c_state, /* unused, inprog, done */ + c_type, /* status, buffer */ + c_secure : 1; /* req came from port < 1024 */ + struct in_addr c_client; + u32 c_xid; + u32 c_proc; + unsigned long c_timestamp; + union { + struct svc_buf u_buffer; + u32 u_status; + } c_u; +}; + +#define c_replbuf c_u.u_buffer +#define c_replstat c_u.u_status + +/* cache entry states */ +enum { + RC_UNUSED, + RC_INPROG, + RC_DONE +}; + +/* return values */ +enum { + RC_DROPIT, + RC_REPLY, + RC_DOIT, + RC_INTR +}; + +/* + * Cache types. + * We may want to add more types one day, e.g. for diropres and + * attrstat replies. Using cache entries with fixed length instead + * of buffer pointers may be more efficient. + */ +enum { + RC_NOCACHE, + RC_REPLSTAT, + RC_REPLBUFF, +}; + +/* + * If requests are retransmitted within this interval, they're dropped. + */ +#define RC_DELAY (HZ/5) + +void nfsd_cache_init(void); +void nfsd_cache_shutdown(void); +int nfsd_cache_lookup(struct svc_rqst *, int); +void nfsd_cache_update(struct svc_rqst *, int, u32 *); + +#endif /* __KERNEL__ */ +#endif /* NFSCACHE_H */ diff -u --recursive --new-file v2.1.31/linux/include/linux/nfsd/const.h linux/include/linux/nfsd/const.h --- v2.1.31/linux/include/linux/nfsd/const.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/nfsd/const.h Fri Apr 4 12:19:14 1997 @@ -0,0 +1,86 @@ +/* + * include/linux/nfsd/nfsconst.h + * + * Various constants related to NFS. + * + * Copyright (C) 1995 Olaf Kirch + */ + +#ifndef __NFSCONST_H__ +#define __NFSCONST_H__ + +#include +#include +#include +#include +#include +#include + +/* + * Maximum protocol version supported by knfsd + */ +#define NFSSVC_MAXVERS 3 + +/* + * Maximum blocksize supported by daemon currently at 8K + */ +#define NFSSVC_MAXBLKSIZE 8192 + +#define NFS2_MAXPATHLEN 1024 +#define NFS2_MAXNAMLEN 255 +#define NFS2_FHSIZE NFS_FHSIZE +#define NFS2_COOKIESIZE 4 + +#define NFS3_MAXPATHLEN PATH_MAX +#define NFS3_MAXNAMLEN NAME_MAX +#define NFS3_FHSIZE NFS_FHSIZE +#define NFS3_COOKIEVERFSIZE 8 +#define NFS3_CREATEVERFSIZE 8 +#define NFS3_WRITEVERFSIZE 8 + +#ifdef __KERNEL__ + +#ifndef NFS_SUPER_MAGIC +# define NFS_SUPER_MAGIC 0x6969 +#endif + +/* + * NFS stats. The good thing with these values is that NFSv3 errors are + * a superset of NFSv2 errors (with the exception of NFSERR_WFLUSH which + * no-one uses anyway), so we can happily mix code as long as we make sure + * no NFSv3 errors are returned to NFSv2 clients. + */ +#define NFS_OK 0 /* v2 v3 */ +#define NFSERR_PERM 1 /* v2 v3 */ +#define NFSERR_NOENT 2 /* v2 v3 */ +#define NFSERR_IO 5 /* v2 v3 */ +#define NFSERR_NXIO 6 /* v2 v3 */ +#define NFSERR_ACCES 13 /* v2 v3 */ +#define NFSERR_EXIST 17 /* v2 v3 */ +#define NFSERR_XDEV 18 /* v3 */ +#define NFSERR_NODEV 19 /* v2 v3 */ +#define NFSERR_NOTDIR 20 /* v2 v3 */ +#define NFSERR_ISDIR 21 /* v2 v3 */ +#define NFSERR_INVAL 22 /* v3 */ +#define NFSERR_FBIG 27 /* v2 v3 */ +#define NFSERR_NOSPC 28 /* v2 v3 */ +#define NFSERR_ROFS 30 /* v2 v3 */ +#define NFSERR_MLINK 31 /* v3 */ +#define NFSERR_NAMETOOLONG 63 /* v2 v3 */ +#define NFSERR_NOTEMPTY 66 /* v2 v3 */ +#define NFSERR_DQUOT 69 /* v2 v3 */ +#define NFSERR_STALE 70 /* v2 v3 */ +#define NFSERR_REMOTE 71 /* v3 */ +#define NFSERR_WFLUSH 99 /* v2 */ +#define NFSERR_BADHANDLE 10001 /* v3 */ +#define NFSERR_NOT_SYNC 10002 /* v3 */ +#define NFSERR_BAD_COOKIE 10003 /* v3 */ +#define NFSERR_NOTSUPP 10004 /* v3 */ +#define NFSERR_TOOSMALL 10005 /* v3 */ +#define NFSERR_SERVERFAULT 10006 /* v3 */ +#define NFSERR_BADTYPE 10007 /* v3 */ +#define NFSERR_JUKEBOX 10008 /* v3 */ + +#endif /* __KERNEL__ */ + +#endif /* __NFSCONST_H__ */ diff -u --recursive --new-file v2.1.31/linux/include/linux/nfsd/debug.h linux/include/linux/nfsd/debug.h --- v2.1.31/linux/include/linux/nfsd/debug.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/nfsd/debug.h Fri Apr 4 12:19:14 1997 @@ -0,0 +1,48 @@ +/* + * linux/include/linux/nfsd/debug.h + * + * Debugging-related stuff for nfsd + * + * Copyright (C) 1995 Olaf Kirch + */ + +#ifndef LINUX_NFSD_DEBUG_H +#define LINUX_NFSD_DEBUG_H + +#include + +/* + * Enable debugging for nfsd. + * Requires RPC_DEBUG. + */ +#ifdef RPC_DEBUG +# define NFSD_DEBUG 1 +#endif + +/* + * knfsd debug flags + */ +#define NFSDDBG_SOCK 0x0001 +#define NFSDDBG_FH 0x0002 +#define NFSDDBG_EXPORT 0x0004 +#define NFSDDBG_SVC 0x0008 +#define NFSDDBG_PROC 0x0010 +#define NFSDDBG_FILEOP 0x0020 +#define NFSDDBG_AUTH 0x0040 +#define NFSDDBG_REPCACHE 0x0080 +#define NFSDDBG_XDR 0x0100 +#define NFSDDBG_LOCKD 0x0200 +#define NFSDDBG_ALL 0x7FFF +#define NFSDDBG_NOCHANGE 0xFFFF + + +#ifdef __KERNEL__ +# undef ifdebug +# ifdef NFSD_DEBUG +# define ifdebug(flag) if (nfsd_debug & NFSDDBG_##flag) +# else +# define ifdebug(flag) if (0) +# endif +#endif /* __KERNEL__ */ + +#endif /* LINUX_NFSD_DEBUG_H */ diff -u --recursive --new-file v2.1.31/linux/include/linux/nfsd/export.h linux/include/linux/nfsd/export.h --- v2.1.31/linux/include/linux/nfsd/export.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/nfsd/export.h Fri Apr 4 12:19:14 1997 @@ -0,0 +1,109 @@ +/* + * include/linux/nfsd/export.h + * + * Public declarations for NFS exports. The definitions for the + * syscall interface are in nfsctl.h + * + * Copyright (C) 1995, 1996 Olaf Kirch + */ + +#ifndef NFSD_EXPORT_H +#define NFSD_EXPORT_H + +#include +#include +#include +#include + +/* + * Important limits for the exports stuff. + */ +#define NFSCLNT_IDMAX 1024 +#define NFSCLNT_ADDRMAX 16 +#define NFSCLNT_KEYMAX 32 + +/* + * Export flags. + */ +#define NFSEXP_READONLY 0x0001 +#define NFSEXP_INSECURE_PORT 0x0002 +#define NFSEXP_ROOTSQUASH 0x0004 +#define NFSEXP_ALLSQUASH 0x0008 +#define NFSEXP_ASYNC 0x0010 +#define NFSEXP_GATHERED_WRITES 0x0020 +#define NFSEXP_UIDMAP 0x0040 +#define NFSEXP_KERBEROS 0x0080 /* not available */ +#define NFSEXP_SUNSECURE 0x0100 +#define NFSEXP_CROSSMNT 0x0200 /* not available */ +#define NFSEXP_ALLFLAGS 0x03FF + + +#ifdef __KERNEL__ + +/* The following are hashtable sizes and must be powers of 2 */ +#define NFSCLNT_EXPMAX 16 + +struct svc_client { + struct svc_client * cl_next; + char cl_ident[NFSCLNT_IDMAX]; + int cl_idlen; + int cl_naddr; + struct in_addr cl_addr[NFSCLNT_ADDRMAX]; + struct svc_uidmap * cl_umap; + struct svc_export * cl_export[NFSCLNT_EXPMAX]; +}; + +struct svc_export { + struct svc_export * ex_next; + char ex_path[NFS_MAXPATHLEN+1]; + struct svc_export * ex_parent; + struct svc_client * ex_client; + int ex_flags; + struct inode * ex_inode; + dev_t ex_dev; + ino_t ex_ino; + uid_t ex_anon_uid; + gid_t ex_anon_gid; +}; + +#define EX_SECURE(exp) (!((exp)->ex_flags & NFSEXP_INSECURE_PORT)) +#define EX_ISSYNC(exp) (!((exp)->ex_flags & NFSEXP_ASYNC)) +#define EX_RDONLY(exp) ((exp)->ex_flags & NFSEXP_READONLY) +#define EX_CROSSMNT(exp) ((exp)->ex_flags & NFSEXP_CROSSMNT) +#define EX_SUNSECURE(exp) ((exp)->ex_flags & NFSEXP_SUNSECURE) +#define EX_WGATHER(exp) ((exp)->ex_flags & NFSEXP_GATHERED_WRITES) + + +/* + * Function declarations + */ +void nfsd_export_init(void); +void nfsd_export_shutdown(void); +void exp_readlock(void); +int exp_writelock(void); +void exp_unlock(void); +struct svc_client * exp_getclient(struct sockaddr_in *sin); +void exp_putclient(struct svc_client *clp); +struct svc_export * exp_get(struct svc_client *clp, dev_t dev, ino_t ino); +int exp_rootfh(struct svc_client *, dev_t, ino_t, + struct knfs_fh *); +int nfserrno(int errno); +void exp_nlmdetach(void); + + +extern __inline__ int +exp_checkaddr(struct svc_client *clp, struct in_addr addr) +{ + struct in_addr *ap = clp->cl_addr; + int i; + + for (i = clp->cl_naddr; i--; ap++) + if (ap->s_addr == addr.s_addr) + return 1; + return 0; +} + +#endif /* __KERNEL__ */ + +#endif /* NFSD_EXPORT_H */ + diff -u --recursive --new-file v2.1.31/linux/include/linux/nfsd/nfsd.h linux/include/linux/nfsd/nfsd.h --- v2.1.31/linux/include/linux/nfsd/nfsd.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/nfsd/nfsd.h Fri Apr 4 12:19:14 1997 @@ -0,0 +1,169 @@ +/* + * linux/include/linux/nfsd/nfsd.h + * + * Hodge-podge collection of knfsd-related stuff. + * I will sort this out later. + * + * Copyright (C) 1995 Olaf Kirch + */ + +#ifndef LINUX_NFSD_NFSD_H +#define LINUX_NFSD_NFSD_H + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* + * nfsd version + */ +#define NFSD_VERSION "0.4" + +/* + * Maximum number of nfsd processes + */ +#define NFSD_MAXSERVS 16 + +#ifdef __KERNEL__ +/* + * Special flags for nfsd_permission. These must be different from MAY_READ, + * MAY_WRITE, and MAY_EXEC. + */ +#define MAY_NOP 0 +#define MAY_SATTR 8 +#define MAY_TRUNC 16 +#if (MAY_SATTR | MAY_TRUNC) & (MAY_READ | MAY_WRITE | MAY_EXEC) +# error "please use a different value for MAY_SATTR or MAY_TRUNC." +#endif +#define MAY_CREATE (MAY_EXEC|MAY_WRITE) +#define MAY_REMOVE (MAY_EXEC|MAY_WRITE|MAY_TRUNC) + +/* + * Callback function for readdir + */ +struct readdir_cd { + struct svc_rqst * rqstp; + struct svc_fh * dirfh; + u32 * buffer; + int buflen; + u32 * offset; /* previous dirent->d_next */ + char plus; /* readdirplus */ + char eob; /* end of buffer */ + char dotonly; +}; +typedef int (*encode_dent_fn)(struct readdir_cd *, const char *, + int, off_t, ino_t); + +/* + * Procedure table for NFSv2 + */ +extern struct svc_procedure nfsd_procedures2[]; +extern struct svc_program nfsd_program; + +/* + * Function prototypes. + */ +int nfsd_svc(unsigned short port, int nrservs); + +void nfsd_racache_init(void); +int nfsd_lookup(struct svc_rqst *, struct svc_fh *, + const char *, int, struct svc_fh *); +int nfsd_setattr(struct svc_rqst *, struct svc_fh *, + struct iattr *); +int nfsd_create(struct svc_rqst *, struct svc_fh *, + char *name, int len, struct iattr *attrs, + int type, dev_t rdev, struct svc_fh *res); +int nfsd_open(struct svc_rqst *, struct svc_fh *, int, + int, struct file *); +void nfsd_close(struct file *); +int nfsd_read(struct svc_rqst *, struct svc_fh *, + loff_t, char *, unsigned long *); +int nfsd_write(struct svc_rqst *, struct svc_fh *, + loff_t, char *, unsigned long, int); +int nfsd_readlink(struct svc_rqst *, struct svc_fh *, + char *, int *); +int nfsd_symlink(struct svc_rqst *, struct svc_fh *, + char *name, int len, char *path, int plen, + struct svc_fh *res); +int nfsd_link(struct svc_rqst *, struct svc_fh *, + char *, int, struct svc_fh *); +int nfsd_rename(struct svc_rqst *, + struct svc_fh *, char *, int, + struct svc_fh *, char *, int); +int nfsd_remove(struct svc_rqst *, + struct svc_fh *, char *, int); +int nfsd_unlink(struct svc_rqst *, struct svc_fh *, int type, + char *name, int len); +int nfsd_truncate(struct svc_rqst *, struct svc_fh *, + unsigned long size); +int nfsd_readdir(struct svc_rqst *, struct svc_fh *, + loff_t, encode_dent_fn, + u32 *buffer, int *countp); +int nfsd_statfs(struct svc_rqst *, struct svc_fh *, + struct statfs *); +int nfsd_notify_change(struct inode *, struct iattr *); +int nfsd_permission(struct svc_export *, struct inode *, int); +void nfsd_write_inode(struct inode *); +struct inode * nfsd_iget(dev_t dev, ino_t ino); +int nfsd_parentdev(dev_t *devp); + +/* + * lockd binding + */ +void nfsd_lockd_init(void); +void nfsd_lockd_shutdown(void); +void nfsd_lockd_unexport(struct svc_client *); + + +#ifndef makedev +#define makedev(maj, min) (((maj) << 8) | (min)) +#endif + +/* + * These variables contain pre-xdr'ed values for faster operation. + * FIXME: should be replaced by macros for big-endian machines. + */ +extern u32 nfs_ok, + nfserr_perm, + nfserr_noent, + nfserr_io, + nfserr_nxio, + nfserr_acces, + nfserr_exist, + nfserr_xdev, + nfserr_nodev, + nfserr_notdir, + nfserr_isdir, + nfserr_inval, + nfserr_fbig, + nfserr_nospc, + nfserr_rofs, + nfserr_mlink, + nfserr_nametoolong, + nfserr_dquot, + nfserr_stale, + nfserr_remote, + nfserr_badhandle, + nfserr_notsync, + nfserr_badcookie, + nfserr_notsupp, + nfserr_toosmall, + nfserr_serverfault, + nfserr_badtype, + nfserr_jukebox; + +/* + * Time of server startup + */ +extern struct timeval nfssvc_boot; + +#endif /* __KERNEL__ */ + +#endif /* LINUX_NFSD_NFSD_H */ diff -u --recursive --new-file v2.1.31/linux/include/linux/nfsd/nfsfh.h linux/include/linux/nfsd/nfsfh.h --- v2.1.31/linux/include/linux/nfsd/nfsfh.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/nfsd/nfsfh.h Fri Apr 4 12:19:14 1997 @@ -0,0 +1,170 @@ +/* + * include/linux/nfsd/nfsfh.h + * + * This file describes the layout of the file handles as passed + * over the wire. + * + * Earlier versions of knfsd used to sign file handles using keyed MD5 + * or SHA. I've removed this code, because it doesn't give you more + * security than blocking external access to port 2049 on your firewall. + * + * Copyright (C) 1995, 1996, 1997 Olaf Kirch + */ + +#ifndef NFSD_FH_H +#define NFSD_FH_H + +#include +#include +#include +#include +#include + +/* + * This is our NFSv2 file handle. + * + * The xdev and xino fields are currently used to transport the dev/ino + * of the exported inode. The xdev field is redundant, though, because + * we do not allow mount point crossing. + */ +struct nfs_fhbase { + dev_t fb_dev; + dev_t fb_xdev; + ino_t fb_ino; + ino_t fb_xino; + __u32 fb_version; +}; + +#define NFS_FH_PADDING (NFS_FHSIZE - sizeof(struct nfs_fhbase)) +struct knfs_fh { + struct nfs_fhbase fh_base; + __u8 fh_cookie[NFS_FH_PADDING]; +}; + +#define fh_dev fh_base.fb_dev +#define fh_xdev fh_base.fb_xdev +#define fh_ino fh_base.fb_ino +#define fh_xino fh_base.fb_xino +#define fh_version fh_base.fb_version + + +#ifdef __KERNEL__ + +/* + * This is the internal representation of an NFS handle used in knfsd. + * pre_mtime/post_version will be used to support wcc_attr's in NFSv3. + */ +typedef struct svc_fh { + struct knfs_fh fh_handle; /* FH data */ + struct svc_export * fh_export; /* export pointer */ + struct inode * fh_inode; /* inode */ + size_t fh_pre_size; /* size before operation */ + time_t fh_pre_mtime; /* mtime before oper */ + time_t fh_pre_ctime; /* ctime before oper */ + unsigned long fh_post_version;/* inode version after oper */ + unsigned char fh_locked; /* inode locked by us */ +} svc_fh; + +/* + * Shorthands for dprintk()'s + */ +#define SVCFH_INO(f) ((f)->fh_handle.fh_ino) +#define SVCFH_DEV(f) ((f)->fh_handle.fh_dev) + +/* + * Function prototypes + */ +u32 fh_lookup(struct svc_rqst *, struct svc_fh *, int, int); +void fh_compose(struct svc_fh *, struct svc_export *, + struct inode *); + +static __inline__ struct svc_fh * +fh_copy(struct svc_fh *dst, struct svc_fh *src) +{ + *dst = *src; + return dst; +} + +static __inline__ struct svc_fh * +fh_init(struct svc_fh *fhp) +{ + memset(fhp, 0, sizeof(*fhp)); + return fhp; +} + +/* + * Lock a file handle/inode + */ +static inline void +fh_lock(struct svc_fh *fhp) +{ + struct inode *inode = fhp->fh_inode; + + /* + dfprintk(FILEOP, "nfsd: fh_lock(%x/%ld) locked = %d\n", + SVCFH_DEV(fhp), SVCFH_INO(fhp), fhp->fh_locked); + */ + if (!fhp->fh_locked) { + down(&inode->i_sem); + if (!fhp->fh_pre_mtime) + fhp->fh_pre_mtime = inode->i_mtime; + fhp->fh_locked = 1; + } +} + +/* + * Unlock a file handle/inode + */ +static inline void +fh_unlock(struct svc_fh *fhp) +{ + struct inode *inode = fhp->fh_inode; + + if (fhp->fh_locked) { + if (!fhp->fh_post_version) + fhp->fh_post_version = inode->i_version; + fhp->fh_locked = 0; + up(&inode->i_sem); + } +} + +/* + * Release an inode + */ +#ifndef NFSD_DEBUG +static inline void +fh_put(struct svc_fh *fhp) +{ + if (fhp->fh_inode) { + fh_unlock(fhp); + iput(fhp->fh_inode); + } +} +#else +#define fh_put(fhp) __fh_put(fhp, __FILE__, __LINE__) + +static inline void +__fh_put(struct svc_fh *fhp, char *file, int line) +{ + struct inode *inode; + + if (!(inode = fhp->fh_inode)) + return; + + if (!inode->i_count) { + printk("nfsd: trying to free free inode in %s:%d\n" + " dev %04x ino %ld, mode %07o\n", + file, line, inode->i_dev, + inode->i_ino, inode->i_mode); + } else { + fh_unlock(fhp); + iput(inode); + } +} +#endif + + + +#endif /* __KERNEL__ */ + +#endif /* NFSD_FH_H */ diff -u --recursive --new-file v2.1.31/linux/include/linux/nfsd/stats.h linux/include/linux/nfsd/stats.h --- v2.1.31/linux/include/linux/nfsd/stats.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/nfsd/stats.h Sun Dec 15 05:45:08 1996 @@ -0,0 +1,27 @@ +/* + * linux/include/nfsd/stats.h + * + * Statistics for NFS server. + * + * Copyright (C) 1995, 1996 Olaf Kirch + */ + +#ifndef LINUX_NFSD_STATS_H +#define LINUX_NFSD_STATS_H + +struct nfsd_stats { + unsigned int rchits; /* repcache hits */ + unsigned int rcmisses; /* repcache hits */ + unsigned int rcnocache; /* uncached reqs */ +}; + +#ifdef __KERNEL__ + +extern struct nfsd_stats nfsdstats; +extern struct svc_stat nfsd_svcstats; + +void nfsd_stat_init(void); +void nfsd_stat_shutdown(void); + +#endif /* __KERNEL__ */ +#endif /* LINUX_NFSD_STATS_H */ diff -u --recursive --new-file v2.1.31/linux/include/linux/nfsd/syscall.h linux/include/linux/nfsd/syscall.h --- v2.1.31/linux/include/linux/nfsd/syscall.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/nfsd/syscall.h Mon Mar 3 01:14:58 1997 @@ -0,0 +1,127 @@ +/* + * include/linux/nfsd/syscall.h + * + * This file holds all declarations for the knfsd syscall interface. + * + * Copyright (C) 1995 Olaf Kirch + */ + +#ifndef NFSD_SYSCALL_H +#define NFSD_SYSCALL_H + +#include +#include +#include +#include +#include +#include +#include + +/* + * Version of the syscall interface + */ +#define NFSCTL_VERSION 0x0201 + +/* + * These are the commands understood by nfsctl(). + */ +#define NFSCTL_SVC 0 /* This is a server process. */ +#define NFSCTL_ADDCLIENT 1 /* Add an NFS client. */ +#define NFSCTL_DELCLIENT 2 /* Remove an NFS client. */ +#define NFSCTL_EXPORT 3 /* export a file system. */ +#define NFSCTL_UNEXPORT 4 /* unexport a file system. */ +#define NFSCTL_UGIDUPDATE 5 /* update a client's uid/gid map. */ +#define NFSCTL_GETFH 6 /* get an fh (used by mountd) */ + + +/* SVC */ +struct nfsctl_svc { + unsigned short svc_port; + int svc_nthreads; +}; + +/* ADDCLIENT/DELCLIENT */ +struct nfsctl_client { + char cl_ident[NFSCLNT_IDMAX+1]; + int cl_naddr; + struct in_addr cl_addrlist[NFSCLNT_ADDRMAX]; + int cl_fhkeytype; + int cl_fhkeylen; + unsigned char cl_fhkey[NFSCLNT_KEYMAX]; +}; + +/* EXPORT/UNEXPORT */ +struct nfsctl_export { + char ex_client[NFSCLNT_IDMAX+1]; + char ex_path[NFS_MAXPATHLEN+1]; + dev_t ex_dev; + ino_t ex_ino; + int ex_flags; + uid_t ex_anon_uid; + gid_t ex_anon_gid; +}; + +/* UGIDUPDATE */ +struct nfsctl_uidmap { + char * ug_ident; + uid_t ug_uidbase; + int ug_uidlen; + uid_t * ug_udimap; + uid_t ug_gidbase; + int ug_gidlen; + gid_t * ug_gdimap; +}; + +/* GETFH */ +struct nfsctl_fhparm { + struct sockaddr gf_addr; + dev_t gf_dev; + ino_t gf_ino; + int gf_version; +}; + +/* + * This is the argument union. + */ +struct nfsctl_arg { + int ca_version; /* safeguard */ + union { + struct nfsctl_svc u_svc; + struct nfsctl_client u_client; + struct nfsctl_export u_export; + struct nfsctl_uidmap u_umap; + struct nfsctl_fhparm u_getfh; + unsigned int u_debug; + } u; +#define ca_svc u.u_svc +#define ca_client u.u_client +#define ca_export u.u_export +#define ca_umap u.u_umap +#define ca_getfh u.u_getfh +#define ca_authd u.u_authd +#define ca_debug u.u_debug +}; + +union nfsctl_res { + struct knfs_fh cr_getfh; + unsigned int cr_debug; +}; + +#ifdef __KERNEL__ +/* + * Kernel syscall implementation. + */ +#ifdef CONFIG_NFSD +extern asmlinkage int sys_nfsservctl(int, struct nfsctl_arg *, + union nfsctl_res *); +#else +# define sys_nfsservctl sys_ni_syscall +#endif +extern int exp_addclient(struct nfsctl_client *ncp); +extern int exp_delclient(struct nfsctl_client *ncp); +extern int exp_export(struct nfsctl_export *nxp); +extern int exp_unexport(struct nfsctl_export *nxp); + +#endif /* __KERNEL__ */ + +#endif /* NFSD_SYSCALL_H */ diff -u --recursive --new-file v2.1.31/linux/include/linux/nfsd/xdr.h linux/include/linux/nfsd/xdr.h --- v2.1.31/linux/include/linux/nfsd/xdr.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/nfsd/xdr.h Fri Mar 14 05:04:32 1997 @@ -0,0 +1,158 @@ +/* + * linux/inxlude/linux/nfsd/xdr.h + * + * XDR types for nfsd. This is mainly a typing exercise. + */ + +#ifndef LINUX_NFSD_H +#define LINUX_NFSD_H + +#include +#include + +struct nfsd_fhandle { + struct svc_fh fh; +}; + +struct nfsd_sattrargs { + struct svc_fh fh; + struct iattr attrs; +}; + +struct nfsd_diropargs { + struct svc_fh fh; + char * name; + int len; +}; + +struct nfsd_readargs { + struct svc_fh fh; + __u32 offset; + __u32 count; + __u32 totalsize; +}; + +struct nfsd_writeargs { + svc_fh fh; + __u32 beginoffset; + __u32 offset; + __u32 totalcount; + __u8 * data; + int len; +}; + +struct nfsd_createargs { + struct svc_fh fh; + char * name; + int len; + struct iattr attrs; +}; + +struct nfsd_renameargs { + struct svc_fh ffh; + char * fname; + int flen; + struct svc_fh tfh; + char * tname; + int tlen; +}; + +struct nfsd_linkargs { + struct svc_fh ffh; + struct svc_fh tfh; + char * tname; + int tlen; +}; + +struct nfsd_symlinkargs { + struct svc_fh ffh; + char * fname; + int flen; + char * tname; + int tlen; + struct iattr attrs; +}; + +struct nfsd_readdirargs { + struct svc_fh fh; + __u32 cookie; + __u32 count; +}; + +struct nfsd_attrstat { + struct svc_fh fh; +}; + +struct nfsd_diropres { + struct svc_fh fh; +}; + +struct nfsd_readlinkres { + int len; +}; + +struct nfsd_readres { + struct svc_fh fh; + unsigned long count; +}; + +struct nfsd_readdirres { + int count; +}; + +struct nfsd_statfsres { + struct statfs stats; +}; + +/* + * Storage requirements for XDR arguments and results. + */ +union nfsd_xdrstore { + struct nfsd_sattrargs sattr; + struct nfsd_diropargs dirop; + struct nfsd_readargs read; + struct nfsd_writeargs write; + struct nfsd_createargs create; + struct nfsd_renameargs rename; + struct nfsd_linkargs link; + struct nfsd_symlinkargs symlink; + struct nfsd_readdirargs readdir; +}; + +#define NFSSVC_XDRSIZE sizeof(union nfsd_xdrstore) + +void nfsd_xdr_init(void); + +int nfssvc_decode_void(struct svc_rqst *, u32 *, void *); +int nfssvc_decode_fhandle(struct svc_rqst *, u32 *, struct svc_fh *); +int nfssvc_decode_sattrargs(struct svc_rqst *, u32 *, + struct nfsd_sattrargs *); +int nfssvc_decode_diropargs(struct svc_rqst *, u32 *, + struct nfsd_diropargs *); +int nfssvc_decode_readargs(struct svc_rqst *, u32 *, + struct nfsd_readargs *); +int nfssvc_decode_writeargs(struct svc_rqst *, u32 *, + struct nfsd_writeargs *); +int nfssvc_decode_createargs(struct svc_rqst *, u32 *, + struct nfsd_createargs *); +int nfssvc_decode_renameargs(struct svc_rqst *, u32 *, + struct nfsd_renameargs *); +int nfssvc_decode_linkargs(struct svc_rqst *, u32 *, + struct nfsd_linkargs *); +int nfssvc_decode_symlinkargs(struct svc_rqst *, u32 *, + struct nfsd_symlinkargs *); +int nfssvc_decode_readdirargs(struct svc_rqst *, u32 *, + struct nfsd_readdirargs *); +int nfssvc_encode_void(struct svc_rqst *, u32 *, void *); +int nfssvc_encode_attrstat(struct svc_rqst *, u32 *, struct nfsd_attrstat *); +int nfssvc_encode_diropres(struct svc_rqst *, u32 *, struct nfsd_diropres *); +int nfssvc_encode_readlinkres(struct svc_rqst *, u32 *, struct nfsd_readlinkres *); +int nfssvc_encode_readres(struct svc_rqst *, u32 *, struct nfsd_readres *); +int nfssvc_encode_statfsres(struct svc_rqst *, u32 *, struct nfsd_statfsres *); +int nfssvc_encode_readdirres(struct svc_rqst *, u32 *, struct nfsd_readdirres *); +int nfssvc_release_fhandle(struct svc_rqst *, u32 *, struct nfsd_fhandle *); + +int nfssvc_encode_entry(struct readdir_cd *, const char *name, + int namlen, off_t offset, ino_t ino); + +#endif /* LINUX_NFSD_H */ diff -u --recursive --new-file v2.1.31/linux/include/linux/nfsd/xdr3.h linux/include/linux/nfsd/xdr3.h --- v2.1.31/linux/include/linux/nfsd/xdr3.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/nfsd/xdr3.h Thu Dec 12 16:35:47 1996 @@ -0,0 +1,268 @@ +/* + * linux/include/linux/nfsd/xdr3.h + * + * XDR types for NFSv3 in nfsd. + * + * Copyright (C) 1996, Olaf Kirch + */ + +#ifndef LINUX_NFSD_XDR3_H +#define LINUX_NFSD_XDR3_H + +#include + +struct nfsd3_sattrargs { + struct svc_fh fh; + struct iattr attrs; + time_t guardtime; +}; + +struct nfsd3_diropargs { + struct svc_fh fh; + char * name; + int len; +}; + +struct nfsd3_accessargs { + struct svc_fh fh; + unsigned int access; +}; + +struct nfsd3_readargs { + struct svc_fh fh; + __u64 offset; + __u32 count; +}; + +struct nfsd3_writeargs { + svc_fh fh; + __u64 offset; + __u32 count; + int stable; + __u8 * data; + int len; +}; + +struct nfsd3_createargs { + struct svc_fh fh; + char * name; + int len; + int createmode; + struct iattr attrs; + __u32 * verf; +}; + +struct nfsd3_mknodargs { + struct svc_fh fh; + char * name; + int len; + __u32 ftype; + __u32 major, minor; + struct iattr attrs; +}; + +struct nfsd3_renameargs { + struct svc_fh ffh; + char * fname; + int flen; + struct svc_fh tfh; + char * tname; + int tlen; +}; + +struct nfsd3_linkargs { + struct svc_fh ffh; + struct svc_fh tfh; + char * tname; + int tlen; +}; + +struct nfsd3_symlinkargs { + struct svc_fh ffh; + char * fname; + int flen; + char * tname; + int tlen; + struct iattr attrs; +}; + +struct nfsd3_readdirargs { + struct svc_fh fh; + __u32 cookie; + __u32 dircount; + __u32 count; + __u32 * verf; +}; + +struct nfsd3_commitargs { + struct svc_fh fh; + __u64 offset; + __u64 count; +}; + +struct nfsd3_attrstat { + __u32 status; + struct svc_fh fh; +}; + +struct nfsd3_lookupres { + __u32 status; + struct svc_fh dirfh; + struct svc_fh fh; +}; + +struct nfsd3_accessres { + __u32 status; + struct svc_fh fh; + __u32 access; +}; + +struct nfsd3_readlinkres { + __u32 status; + struct svc_fh fh; + __u32 len; +}; + +struct nfsd3_readres { + __u32 status; + struct svc_fh fh; + unsigned long count; + int eof; +}; + +struct nfsd3_writeres { + __u32 status; + struct svc_fh fh; + unsigned long count; + int committed; +}; + +struct nfsd3_createres { + __u32 status; + struct svc_fh dirfh; + struct svc_fh fh; +}; + +struct nfsd3_renameres { + __u32 status; + struct svc_fh ffh; + struct svc_fh tfh; +}; + +struct nfsd3_linkres { + __u32 status; + struct svc_fh tfh; + struct svc_fh fh; +}; + +struct nfsd3_readdirres { + __u32 status; + struct svc_fh fh; + __u32 * list_end; +}; + +struct nfsd3_statfsres { + __u32 status; + struct statfs stats; + __u32 invarsec; +}; + +struct nfsd3_fsinfores { + __u32 status; + __u32 f_rtmax; + __u32 f_rtpref; + __u32 f_rtmult; + __u32 f_wtmax; + __u32 f_wtpref; + __u32 f_wtmult; + __u32 f_dtpref; + __u64 f_maxfilesize; + __u32 f_properties; +}; + +struct nfsd3_pathconfres { + __u32 status; + __u32 p_link_max; + __u32 p_name_max; + __u32 p_case_insensitive; + __u32 p_case_preserving; +}; + +struct nfsd3_commitres { + __u32 status; + struct svc_fh fh; +}; + +/* dummy type for release */ +struct nfsd3_fhandle2 { + __u32 dummy; + struct svc_fh fh1; + struct svc_fh fh2; +}; + +/* + * Storage requirements for XDR arguments and results. + */ +union nfsd3_xdrstore { + struct nfsd3_sattrargs sattrargs; + struct nfsd3_diropargs diropargs; + struct nfsd3_readargs readargs; + struct nfsd3_writeargs writeargs; + struct nfsd3_createargs createargs; + struct nfsd3_renameargs renameargs; + struct nfsd3_linkargs linkargs; + struct nfsd3_symlinkargs symlinkargs; + struct nfsd3_readdirargs readdirargs; + struct nfsd3_lookupres lookupres; + struct nfsd3_accessres accessres; + struct nfsd3_readlinkres readlinkres; + struct nfsd3_readres readres; + struct nfsd3_writeres writeres; + struct nfsd3_createres createres; + struct nfsd3_renameres renameres; + struct nfsd3_linkres linkres; + struct nfsd3_readdirres readdirres; + struct nfsd3_statfsres statfsres; + struct nfsd3_fsinfores fsinfores; + struct nfsd3_pathconfres pathconfres; + struct nfsd3_commitres commitres; +}; + +#define NFS3_SVC_XDRSIZE sizeof(union nfsd3_xdrstore) + +void nfsxdr_init(void); + +int nfs3svc_decode_fhandle(struct svc_rqst *, u32 *, struct svc_fh *); +int nfs3svc_decode_sattr3args(struct svc_rqst *, u32 *, + struct nfsd3_sattrargs *); +int nfs3svc_decode_dirop3args(struct svc_rqst *, u32 *, + struct nfsd3_diropargs *); +int nfs3svc_decode_read3args(struct svc_rqst *, u32 *, + struct nfsd3_readargs *); +int nfs3svc_decode_write3args(struct svc_rqst *, u32 *, + struct nfsd3_writeargs *); +int nfs3svc_decode_create3args(struct svc_rqst *, u32 *, + struct nfsd3_createargs *); +int nfs3svc_decode_rename3args(struct svc_rqst *, u32 *, + struct nfsd3_renameargs *); +int nfs3svc_decode_link3args(struct svc_rqst *, u32 *, + struct nfsd3_linkargs *); +int nfs3svc_decode_symlink3args(struct svc_rqst *, u32 *, + struct nfsd3_symlinkargs *); +int nfs3svc_decode_readdir3args(struct svc_rqst *, u32 *, + struct nfsd3_readdirargs *); +int nfs3svc_encode_readlinkres(struct svc_rqst *, u32 *, + struct nfsd3_readlinkres *); +int nfs3svc_encode_readres(struct svc_rqst *, u32 *, struct nfsd3_readres *); +int nfs3svc_encode_statfsres(struct svc_rqst *, u32 *, + struct nfsd3_statfsres *); +int nfs3svc_encode_readdirres(struct svc_rqst *, u32 *, + struct nfsd3_readdirres *); +int nfs3svc_release_fhandle(struct svc_rqst *, u32 *, + struct nfsd_fhandle *); +int nfs3svc_release_fhandle2(struct svc_rqst *, u32 *, + struct nfsd3_fhandle2 *); +int nfs3svc_encode_entry(struct readdir_cd *, const char *name, + int namlen, unsigned long offset, ino_t ino); + +#endif /* LINUX_NFSD_XDR3_H */ diff -u --recursive --new-file v2.1.31/linux/include/linux/sunrpc/auth.h linux/include/linux/sunrpc/auth.h --- v2.1.31/linux/include/linux/sunrpc/auth.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/sunrpc/auth.h Fri Apr 4 12:19:02 1997 @@ -0,0 +1,99 @@ +/* + * linux/include/linux/auth.h + * + * Declarations for the RPC authentication machinery. + * + * Copyright (C) 1996, Olaf Kirch + */ + +#ifndef _LINUX_SUNRPC_AUTH_H +#define _LINUX_SUNRPC_AUTH_H + +#ifdef __KERNEL__ + +#include +#include + + +/* + * Client user credentials + */ +struct rpc_cred { + struct rpc_cred * cr_next; /* linked list */ + unsigned long cr_expire; /* when to gc */ + unsigned short cr_count; /* ref count */ + unsigned short cr_flags; /* various flags */ + + uid_t cr_uid; + + /* per-flavor data */ +}; +#define RPCAUTH_CRED_LOCKED 0x0001 +#define RPCAUTH_CRED_UPTODATE 0x0002 +#define RPCAUTH_CRED_DEAD 0x0004 + +/* + * Client authentication handle + */ +#define RPC_CREDCACHE_NR 8 +struct rpc_auth { + struct rpc_cred * au_credcache[RPC_CREDCACHE_NR]; + unsigned long au_expire; /* cache expiry interval */ + unsigned long au_nextgc; /* next garbage collection */ + unsigned int au_cslack; /* call cred size estimate */ + unsigned int au_rslack; /* reply verf size guess */ + unsigned int au_flags; /* various flags */ + struct rpc_authops * au_ops; /* operations */ + + /* per-flavor data */ +}; +#define RPC_AUTH_PROC_CREDS 0x0010 /* process creds (including + * uid/gid, fs[ug]id, gids) + */ + +/* + * Client authentication ops + */ +struct rpc_authops { + unsigned int au_flavor; /* flavor (RPC_AUTH_*) */ +#ifdef RPC_DEBUG + char * au_name; +#endif + struct rpc_auth * (*create)(struct rpc_clnt *); + void (*destroy)(struct rpc_auth *); + + struct rpc_cred * (*crcreate)(struct rpc_task *); + void (*crdestroy)(struct rpc_cred *); + + int (*crmatch)(struct rpc_task *, struct rpc_cred*); + u32 * (*crmarshal)(struct rpc_task *, u32 *, int); + int (*crrefresh)(struct rpc_task *); + u32 * (*crvalidate)(struct rpc_task *, u32 *); +}; + +extern struct rpc_authops authunix_ops; +extern struct rpc_authops authnull_ops; +#ifdef CONFIG_SUNRPC_SECURE +extern struct rpc_authops authdes_ops; +#endif + +int rpcauth_register(struct rpc_authops *); +int rpcauth_unregister(struct rpc_authops *); +struct rpc_auth * rpcauth_create(unsigned int, struct rpc_clnt *); +void rpcauth_destroy(struct rpc_auth *); +struct rpc_cred * rpcauth_lookupcred(struct rpc_task *); +void rpcauth_holdcred(struct rpc_task *); +void rpcauth_releasecred(struct rpc_task *); +int rpcauth_matchcred(struct rpc_task *, struct rpc_cred *); +u32 * rpcauth_marshcred(struct rpc_task *, u32 *); +u32 * rpcauth_checkverf(struct rpc_task *, u32 *); +int rpcauth_refreshcred(struct rpc_task *); +void rpcauth_invalcred(struct rpc_task *); +int rpcauth_uptodatecred(struct rpc_task *); +void rpcauth_init_credcache(struct rpc_auth *); +void rpcauth_free_credcache(struct rpc_auth *); +void rpcauth_insert_credcache(struct rpc_auth *, + struct rpc_cred *); + +#endif /* __KERNEL__ */ +#endif /* _LINUX_SUNRPC_AUTH_H */ diff -u --recursive --new-file v2.1.31/linux/include/linux/sunrpc/clnt.h linux/include/linux/sunrpc/clnt.h --- v2.1.31/linux/include/linux/sunrpc/clnt.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/sunrpc/clnt.h Fri Apr 4 12:19:02 1997 @@ -0,0 +1,130 @@ +/* + * linux/include/linux/sunrpc/clnt.h + * + * Declarations for the high-level RPC client interface + * + * Copyright (C) 1995, 1996, Olaf Kirch + */ + +#ifndef _LINUX_SUNRPC_CLNT_H +#define _LINUX_SUNRPC_CLNT_H + +#include +#include +#include +#include +#include +#include + +/* + * This defines an RPC port mapping + */ +struct rpc_portmap { + __u32 pm_prog; + __u32 pm_vers; + __u32 pm_prot; + __u32 pm_port; +}; + +/* + * The high-level client handle + */ +struct rpc_clnt { + struct rpc_xprt * cl_xprt; /* transport */ + struct rpc_procinfo * cl_procinfo; /* procedure info */ + u32 cl_maxproc; /* max procedure number */ + + char * cl_server; /* server machine name */ + char * cl_protname; /* protocol name */ + struct rpc_auth * cl_auth; /* authenticator */ + struct rpc_portmap cl_pmap; /* port mapping */ + struct rpc_stat * cl_stats; /* statistics */ + + unsigned int cl_softrtry : 1,/* soft timeouts */ + cl_intr : 1,/* interruptible */ + cl_chatty : 1,/* be verbose */ + cl_autobind : 1,/* use getport() */ + cl_binding : 1,/* doing a getport() */ + cl_oneshot : 1,/* dispose after use */ + cl_dead : 1;/* abandoned */ + unsigned long cl_hardmax; /* max hard timeout */ + + struct rpc_wait_queue cl_bindwait; /* waiting on getport() */ + unsigned int cl_users; /* number of references */ +}; +#define cl_timeout cl_xprt->timeout +#define cl_prog cl_pmap.pm_prog +#define cl_vers cl_pmap.pm_vers +#define cl_port cl_pmap.pm_port +#define cl_prot cl_pmap.pm_prot + +/* + * General RPC program info + */ +#define RPC_MAXVERSION 4 +struct rpc_program { + char * name; /* protocol name */ + u32 number; /* program number */ + unsigned int nrvers; /* number of versions */ + struct rpc_version ** version; /* version array */ + struct rpc_stat * stats; /* statistics */ +}; + +struct rpc_version { + u32 number; /* version number */ + unsigned int nrprocs; /* number of procs */ + struct rpc_procinfo * procs; /* procedure array */ +}; + +/* + * Procedure information + */ +struct rpc_procinfo { + char * p_procname; /* procedure name */ + kxdrproc_t p_encode; /* XDR encode function */ + kxdrproc_t p_decode; /* XDR decode function */ + unsigned int p_bufsiz; /* req. buffer size */ + unsigned int p_count; /* call count */ +}; + +#define rpcproc_bufsiz(clnt, proc) ((clnt)->cl_procinfo[proc].p_bufsiz) +#define rpcproc_encode(clnt, proc) ((clnt)->cl_procinfo[proc].p_encode) +#define rpcproc_decode(clnt, proc) ((clnt)->cl_procinfo[proc].p_decode) +#define rpcproc_name(clnt, proc) ((clnt)->cl_procinfo[proc].p_procname) +#define rpcproc_count(clnt, proc) ((clnt)->cl_procinfo[proc].p_count) + +#define RPC_CONGESTED(clnt) (RPCXPRT_CONGESTED((clnt)->cl_xprt)) +#define RPC_PEERADDR(clnt) (&(clnt)->cl_xprt->addr) + +#ifdef __KERNEL__ + +struct rpc_clnt *rpc_create_client(struct rpc_xprt *xprt, char *servname, + struct rpc_program *info, + u32 version, int authflavor); +int rpc_shutdown_client(struct rpc_clnt *); +int rpc_destroy_client(struct rpc_clnt *); +void rpc_release_client(struct rpc_clnt *); +void rpc_getport(struct rpc_task *, struct rpc_clnt *); +int rpc_register(u32, u32, int, unsigned short, int *); + +int rpc_call(struct rpc_clnt *clnt, u32 proc, + void *argp, void *resp, int flags); +int rpc_call_async(struct rpc_task *task, u32 proc, + void *argp, void *resp, int flags); +void rpc_call_setup(struct rpc_task *task, u32 proc, + void *argp, void *resp, int flags); +int rpc_do_call(struct rpc_clnt *clnt, u32 proc, + void *argp, void *resp, int flags, + rpc_action callback, void *clntdata); +void rpc_restart_call(struct rpc_task *); +#define rpc_call(clnt, proc, argp, resp, flags) \ + rpc_do_call(clnt, proc, argp, resp, flags, NULL, NULL) + +extern __inline__ void +rpc_set_timeout(struct rpc_clnt *clnt, unsigned int retr, unsigned long incr) +{ + xprt_set_timeout(&clnt->cl_timeout, retr, incr); +} + +#endif /* __KERNEL__ */ +#endif /* _LINUX_SUNRPC_CLNT_H */ diff -u --recursive --new-file v2.1.31/linux/include/linux/sunrpc/debug.h linux/include/linux/sunrpc/debug.h --- v2.1.31/linux/include/linux/sunrpc/debug.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/sunrpc/debug.h Fri Apr 4 11:15:59 1997 @@ -0,0 +1,91 @@ +/* + * linux/include/linux/sunrpc/debug.h + * + * Debugging support for sunrpc module + * + * Copyright (C) 1996, Olaf Kirch + */ + +#ifndef _LINUX_SUNRPC_DEBUG_H_ +#define _LINUX_SUNRPC_DEBUG_H_ + +#include +#include + +/* + * Enable RPC debugging/profiling. + */ +#define RPC_DEBUG +/* #define RPC_PROFILE */ + +/* + * RPC debug facilities + */ +#define RPCDBG_XPRT 0x0001 +#define RPCDBG_CALL 0x0002 +#define RPCDBG_DEBUG 0x0004 +#define RPCDBG_NFS 0x0008 +#define RPCDBG_AUTH 0x0010 +#define RPCDBG_PMAP 0x0020 +#define RPCDBG_SCHED 0x0040 +#define RPCDBG_SVCSOCK 0x0100 +#define RPCDBG_SVCDSP 0x0200 +#define RPCDBG_MISC 0x0400 +#define RPCDBG_ALL 0x7fff + +#ifdef __KERNEL__ + +/* + * Debugging macros etc + */ +#ifdef RPC_DEBUG +extern unsigned int rpc_debug; +extern unsigned int nfs_debug; +extern unsigned int nfsd_debug; +extern unsigned int nlm_debug; +#endif + +#define dprintk(args...) dfprintk(FACILITY, ## args) + +#undef ifdebug +#ifdef RPC_DEBUG +# define ifdebug(fac) if (rpc_debug & RPCDBG_##fac) +# define dfprintk(fac, args...) do { ifdebug(fac) printk(## args); } while(0) +# define RPC_IFDEBUG(x) x +#else +# define dfprintk(fac, args...) do ; while (0) +# define RPC_IFDEBUG(x) +#endif + +#ifdef RPC_PROFILE +# define pprintk(args...) printk(## args) +#else +# define pprintk(args...) do ; while (0) +#endif + +/* + * Sysctl interface for RPC debugging + */ +#ifdef RPC_DEBUG +void rpc_register_sysctl(void); +void rpc_unregister_sysctl(void); +#endif + +#endif /* __KERNEL__ */ + +/* + * Declarations for the sysctl debug interface, which allows to read or + * change the debug flags for rpc, nfs, nfsd, and lockd. Since the sunrpc + * module currently registers its sysctl table dynamically, the sysctl path + * for module FOO is . + */ +#define CTL_SUNRPC 7249 /* arbitrary and hopefully unused */ + +enum { + CTL_RPCDEBUG = 1, + CTL_NFSDEBUG, + CTL_NFSDDEBUG, + CTL_NLMDEBUG, +}; + +#endif /* _LINUX_SUNRPC_DEBUG_H_ */ diff -u --recursive --new-file v2.1.31/linux/include/linux/sunrpc/msg_prot.h linux/include/linux/sunrpc/msg_prot.h --- v2.1.31/linux/include/linux/sunrpc/msg_prot.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/sunrpc/msg_prot.h Sun Oct 13 07:58:51 1996 @@ -0,0 +1,62 @@ +/* + * linux/include/net/sunrpc/msg_prot.h + * + * Copyright (C) 1996, Olaf Kirch + */ + +#ifndef _LINUX_SUNRPC_MSGPROT_H_ +#define _LINUX_SUNRPC_MSGPROT_H_ + +#ifdef __KERNEL__ /* user programs should get these from the rpc header files */ + +#define RPC_VERSION 2 + +enum rpc_auth_flavor { + RPC_AUTH_NULL = 0, + RPC_AUTH_UNIX = 1, + RPC_AUTH_SHORT = 2, + RPC_AUTH_DES = 3, + RPC_AUTH_KRB = 4, +}; + +enum rpc_msg_type { + RPC_CALL = 0, + RPC_REPLY = 1 +}; + +enum rpc_reply_stat { + RPC_MSG_ACCEPTED = 0, + RPC_MSG_DENIED = 1 +}; + +enum rpc_accept_stat { + RPC_SUCCESS = 0, + RPC_PROG_UNAVAIL = 1, + RPC_PROG_MISMATCH = 2, + RPC_PROC_UNAVAIL = 3, + RPC_GARBAGE_ARGS = 4, + RPC_SYSTEM_ERR = 5 +}; + +enum rpc_reject_stat { + RPC_MISMATCH = 0, + RPC_AUTH_ERROR = 1 +}; + +enum rpc_auth_stat { + RPC_AUTH_OK = 0, + RPC_AUTH_BADCRED = 1, + RPC_AUTH_REJECTEDCRED = 2, + RPC_AUTH_BADVERF = 3, + RPC_AUTH_REJECTEDVERF = 4, + RPC_AUTH_TOOWEAK = 5 +}; + +#define RPC_PMAP_PROGRAM 100000 +#define RPC_PMAP_VERSION 2 +#define RPC_PMAP_PORT 111 + +#define RPC_MAXNETNAMELEN 256 + +#endif /* __KERNEL__ */ +#endif /* _LINUX_SUNRPC_MSGPROT_H_ */ diff -u --recursive --new-file v2.1.31/linux/include/linux/sunrpc/sched.h linux/include/linux/sunrpc/sched.h --- v2.1.31/linux/include/linux/sunrpc/sched.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/sunrpc/sched.h Fri Apr 4 11:18:20 1997 @@ -0,0 +1,170 @@ +/* + * linux/include/linux/sunrpc/sched.h + * + * Scheduling primitives for kernel Sun RPC. + * + * Copyright (C) 1996, Olaf Kirch + */ + +#ifndef _LINUX_SUNRPC_SCHED_H_ +#define _LINUX_SUNRPC_SCHED_H_ + +#include +#include +#include + +/* + * Define this if you want to test the fast scheduler for async calls. + * This is still experimental and may not work. + */ +#undef CONFIG_RPC_FASTSCHED + +/* + * This is the RPC task struct + */ +struct rpc_task { + struct rpc_task * tk_prev; /* wait queue links */ + struct rpc_task * tk_next; +#ifdef RPC_DEBUG + unsigned long tk_magic; /* 0xf00baa */ +#endif + struct rpc_task * tk_next_task; /* global list of tasks */ + struct rpc_task * tk_prev_task; /* global list of tasks */ + struct rpc_clnt * tk_client; /* RPC client */ + struct rpc_rqst * tk_rqstp; /* RPC request */ + struct rpc_cred * tk_cred; /* RPC credentials */ + int tk_status; /* result of last operation */ + struct rpc_wait_queue * tk_rpcwait; /* RPC wait queue we're on */ + + /* + * RPC call state + */ + u32 tk_proc; /* procedure number */ + u32 * tk_buffer; /* XDR buffer */ + void * tk_argp; /* argument storage */ + void * tk_resp; /* result storage */ + u8 tk_garb_retry, + tk_cred_retry, + tk_suid_retry; + + /* + * callback to be executed after waking up + * action next procedure for async tasks + * exit exit async task and report to caller + */ + void (*tk_callback)(struct rpc_task *); + void (*tk_action)(struct rpc_task *); + void (*tk_exit)(struct rpc_task *); + void * tk_calldata; + + /* + * tk_timer is used for async processing by the RPC scheduling + * primitives. You should not access this directly unless + * you have a pathological interest in kernel oopses. + */ + struct timer_list tk_timer; /* kernel timer */ + struct wait_queue * tk_wait; /* sync: sleep on this q */ + unsigned long tk_timeout; /* timeout for rpc_sleep() */ + unsigned short tk_flags; /* misc flags */ +#ifdef RPC_DEBUG + unsigned short tk_pid; /* debugging aid */ +#endif +}; +#define tk_auth tk_client->cl_auth +#define tk_xprt tk_client->cl_xprt + +typedef void (*rpc_action)(struct rpc_task *); + +/* + * RPC task flags + */ +#define RPC_TASK_RUNNING 0x0001 /* is running */ +#define RPC_TASK_ASYNC 0x0002 /* is an async task */ +#define RPC_TASK_CALLBACK 0x0004 /* invoke callback */ +#define RPC_TASK_SWAPPER 0x0008 /* is swapping in/out */ +#define RPC_TASK_SETUID 0x0010 /* is setuid process */ +#define RPC_TASK_CHILD 0x0020 /* is child of other task */ +#define RPC_CALL_REALUID 0x0040 /* try using real uid */ +#define RPC_CALL_MAJORSEEN 0x0080 /* major timeout seen */ +#define RPC_TASK_ROOTCREDS 0x0100 /* force root creds */ +#define RPC_TASK_DYNAMIC 0x0200 /* task was kmalloc'ed */ +#define RPC_TASK_KILLED 0x0400 /* task was killed */ + +#define RPC_IS_RUNNING(t) ((t)->tk_flags & RPC_TASK_RUNNING) +#define RPC_IS_ASYNC(t) ((t)->tk_flags & RPC_TASK_ASYNC) +#define RPC_IS_SETUID(t) ((t)->tk_flags & RPC_TASK_SETUID) +#define RPC_IS_CHILD(t) ((t)->tk_flags & RPC_TASK_CHILD) +#define RPC_IS_SWAPPER(t) ((t)->tk_flags & RPC_TASK_SWAPPER) +#define RPC_DO_CALLBACK(t) ((t)->tk_flags & RPC_TASK_CALLBACK) +#define RPC_DO_ROOTOVERRIDE(t) ((t)->tk_flags & RPC_TASK_ROOTCREDS) +#define RPC_ASSASSINATED(t) ((t)->tk_flags & RPC_TASK_KILLED) + +/* + * RPC synchronization objects + */ +struct rpc_wait_queue { + struct rpc_task * task; +#ifdef RPC_DEBUG + char * name; +#endif +}; + +#ifndef RPC_DEBUG +# define RPC_INIT_WAITQ(name) ((struct rpc_wait_queue) { NULL }) +#else +# define RPC_INIT_WAITQ(name) ((struct rpc_wait_queue) { NULL, name }) +#endif + +/* + * Function prototypes + */ +struct rpc_task *rpc_new_task(struct rpc_clnt *, rpc_action, int flags); +struct rpc_task *rpc_new_child(struct rpc_clnt *, struct rpc_task *parent); +void rpc_init_task(struct rpc_task *, struct rpc_clnt *, + rpc_action exitfunc, int flags); +void rpc_release_task(struct rpc_task *); +void rpc_killall_tasks(struct rpc_clnt *); +void rpc_execute(struct rpc_task *); +void rpc_run_child(struct rpc_task *parent, struct rpc_task *child, + rpc_action action); +void rpc_add_wait_queue(struct rpc_wait_queue *, struct rpc_task *); +void rpc_remove_wait_queue(struct rpc_task *); +void rpc_sleep_on(struct rpc_wait_queue *, struct rpc_task *, + rpc_action action, rpc_action timer); +void rpc_cond_wait(struct rpc_wait_queue *, struct rpc_task *, + unsigned char *, + rpc_action action, rpc_action timer); +void rpc_wake_up_task(struct rpc_task *); +void rpc_wake_up(struct rpc_wait_queue *); +struct rpc_task *rpc_wake_up_next(struct rpc_wait_queue *); +void rpc_wake_up_status(struct rpc_wait_queue *, int); +void rpc_add_timer(struct rpc_task *, rpc_action); +void rpc_del_timer(struct rpc_task *); +void rpc_delay(struct rpc_task *, unsigned long); +void * rpc_allocate(unsigned int flags, unsigned int); +void rpc_free(void *); +void rpciod_up(void); +void rpciod_down(void); + +extern __inline__ void * +rpc_malloc(struct rpc_task *task, unsigned int size) +{ + return rpc_allocate(task->tk_flags, size); +} + +extern __inline__ void +rpc_exit(struct rpc_task *task, int status) +{ + task->tk_status = status; + task->tk_action = NULL; +} + +#ifdef RPC_DEBUG +extern __inline__ char * +rpc_qname(struct rpc_wait_queue *q) +{ + return q->name? q->name : "unknown"; +} +#endif + +#endif /* _LINUX_SUNRPC_SCHED_H_ */ diff -u --recursive --new-file v2.1.31/linux/include/linux/sunrpc/stats.h linux/include/linux/sunrpc/stats.h --- v2.1.31/linux/include/linux/sunrpc/stats.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/sunrpc/stats.h Fri Apr 4 12:19:02 1997 @@ -0,0 +1,59 @@ +/* + * linux/include/linux/sunrpc/stats.h + * + * Client statistics collection for SUN RPC + * + * Copyright (C) 1996 Olaf Kirch + */ + +#ifndef _LINUX_SUNRPC_STATS_H +#define _LINUX_SUNRPC_STATS_H + +#include + +struct rpc_stat { + struct rpc_stat * next; + struct proc_dir_entry * entry; + struct rpc_program * program; + + unsigned int netcnt, + netudpcnt, + nettcpcnt, + nettcpconn, + netreconn; + unsigned int rpccnt, + rpcretrans, + rpcauthrefresh, + rpcgarbage; +}; + +struct svc_stat { + struct svc_stat * next; + struct proc_dir_entry * entry; + struct svc_program * program; + + unsigned int netcnt, + netudpcnt, + nettcpcnt, + nettcpconn; + unsigned int rpccnt, + rpcbadfmt, + rpcbadauth, + rpcbadclnt; +}; + +void rpcstat_init(void); +void rpcstat_exit(void); + +void rpcstat_register(struct rpc_stat *); +void rpcstat_unregister(struct rpc_stat *); +int rpcstat_get_info(struct rpc_stat *, char *, char **, + off_t, int); +void rpcstat_zero_info(struct rpc_program *); +void svcstat_register(struct svc_stat *); +void svcstat_unregister(struct svc_stat *); +int svcstat_get_info(struct svc_stat *, char *, char **, + off_t, int); +void svcstat_zero_info(struct svc_program *); + +#endif /* _LINUX_SUNRPC_STATS_H */ diff -u --recursive --new-file v2.1.31/linux/include/linux/sunrpc/svc.h linux/include/linux/sunrpc/svc.h --- v2.1.31/linux/include/linux/sunrpc/svc.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/sunrpc/svc.h Fri Apr 4 11:38:27 1997 @@ -0,0 +1,180 @@ +/* + * linux/include/linux/sunrpc/svc.h + * + * RPC server declarations. + * + * Copyright (C) 1995, 1996 Olaf Kirch + */ + + +#ifndef SUNRPC_SVC_H +#define SUNRPC_SVC_H + +#include +#include +#include + +/* + * RPC service. + * + * An RPC service is a ``daemon,'' possibly multithreaded, which + * receives and processes incoming RPC messages. + * It has one or more transport sockets associated with it, and maintains + * a list of idle threads waiting for input. + * + * We currently do not support more than one RPC program per daemon. + */ +struct svc_serv { + struct svc_rqst * sv_threads; /* idle server threads */ + struct svc_sock * sv_sockets; /* pending sockets */ + struct svc_program * sv_program; /* RPC program */ + struct svc_stat * sv_stats; /* RPC statistics */ + unsigned int sv_nrthreads; /* # of server threads */ + unsigned int sv_bufsz; /* datagram buffer size */ + unsigned int sv_xdrsize; /* XDR buffer size */ + + struct svc_sock * sv_allsocks; /* all sockets */ + + char * sv_name; /* service name */ +}; + +/* + * Maximum payload size supported by a kernel RPC server. + * This is use to determine the max number of pages nfsd is + * willing to return in a single READ operation. + */ +#define RPCSVC_MAXPAYLOAD 16384u + +/* + * Buffer to store RPC requests or replies in. + * Each server thread has one of these beasts. + * + * Area points to the allocated memory chunk currently owned by the + * buffer. Base points to the buffer containing the request, which is + * different from area when directly reading from an sk_buff. buf is + * the current read/write position while processing an RPC request. + * + * The array of iovecs can hold additional data that the server process + * may not want to copy into the RPC reply buffer, but pass to the + * network sendmsg routines directly. The prime candidate for this + * will of course be NFS READ operations, but one might also want to + * do something about READLINK and READDIR. It might be worthwhile + * to implement some generic readdir cache in the VFS layer... + * + * On the receiving end of the RPC server, the iovec may be used to hold + * the list of IP fragments once we get to process fragmented UDP + * datagrams directly. + */ +#define RPCSVC_MAXIOV ((RPCSVC_MAXPAYLOAD+PAGE_SIZE-1)/PAGE_SIZE + 1) +struct svc_buf { + u32 * area; /* allocated memory */ + u32 * base; /* base of RPC datagram */ + int buflen; /* total length of buffer */ + u32 * buf; /* read/write pointer */ + int len; /* current end of buffer */ + + /* iovec for zero-copy NFS READs */ + struct iovec iov[RPCSVC_MAXIOV]; + int nriov; +}; +#define svc_getlong(argp, val) { (val) = *(argp)->buf++; (argp)->len--; } +#define svc_putlong(resp, val) { *(resp)->buf++ = (val); (resp)->len++; } + +/* + * The context of a single thread, including the request currently being + * processed. + * NOTE: First two items must be prev/next. + */ +struct svc_rqst { + struct svc_rqst * rq_prev; /* idle list */ + struct svc_rqst * rq_next; + struct svc_sock * rq_sock; /* socket */ + struct sockaddr_in rq_addr; /* peer address */ + int rq_addrlen; + + struct svc_serv * rq_server; /* RPC service definition */ + struct svc_procedure * rq_procinfo; /* procedure info */ + struct svc_cred rq_cred; /* auth info */ + struct sk_buff * rq_skbuff; /* fast recv inet buffer */ + struct svc_buf rq_defbuf; /* default buffer */ + struct svc_buf rq_argbuf; /* argument buffer */ + struct svc_buf rq_resbuf; /* result buffer */ + u32 rq_xid; /* transmission id */ + u32 rq_prog; /* program number */ + u32 rq_vers; /* program version */ + u32 rq_proc; /* procedure number */ + u32 rq_prot; /* IP protocol */ + unsigned short rq_verfed : 1, /* reply has verifier */ + rq_userset : 1, /* auth->setuser OK */ + rq_secure : 1; /* secure port */ + + void * rq_argp; /* decoded arguments */ + void * rq_resp; /* xdr'd results */ + + /* Catering to nfsd */ + struct svc_client * rq_client; /* RPC peer info */ + struct svc_cacherep * rq_cacherep; /* cache info */ + + struct wait_queue * rq_wait; /* synchronozation */ +}; + +/* + * RPC program + */ +struct svc_program { + u32 pg_prog; /* program number */ + unsigned int pg_lovers; /* lowest version */ + unsigned int pg_hivers; /* lowest version */ + unsigned int pg_nvers; /* number of versions */ + struct svc_version ** pg_vers; /* version array */ + char * pg_name; /* service name */ + struct svc_stat * pg_stats; /* rpc statistics */ +}; + +/* + * RPC program version + */ +struct svc_version { + u32 vs_vers; /* version number */ + u32 vs_nproc; /* number of procedures */ + struct svc_procedure * vs_proc; /* per-procedure info */ + + /* Override dispatch function (e.g. when caching replies). + * A return value of 0 means drop the request. + * vs_dispatch == NULL means use default dispatcher. + */ + int (*vs_dispatch)(struct svc_rqst *, u32 *); +}; + +/* + * RPC procedure info + */ +typedef int (*svc_procfunc)(struct svc_rqst *, void *argp, void *resp); +struct svc_procedure { + svc_procfunc pc_func; /* process the request */ + kxdrproc_t pc_decode; /* XDR decode args */ + kxdrproc_t pc_encode; /* XDR encode result */ + kxdrproc_t pc_release; /* XDR free result */ + unsigned int pc_argsize; /* argument struct size */ + unsigned int pc_ressize; /* result struct size */ + unsigned int pc_count; /* call count */ + unsigned int pc_cachetype; /* cache info (NFS) */ +}; + +/* + * This is the RPC server thread function prototype + */ +typedef void (*svc_thread_fn)(struct svc_rqst *); + +/* + * Function prototypes. + */ +struct svc_serv * svc_create(struct svc_program *, unsigned int, unsigned int); +int svc_create_thread(svc_thread_fn, struct svc_serv *); +void svc_exit_thread(struct svc_rqst *); +void svc_destroy(struct svc_serv *); +int svc_process(struct svc_serv *, struct svc_rqst *); +int svc_register(struct svc_serv *, int, unsigned short); +void svc_wake_up(struct svc_serv *); + +#endif /* SUNRPC_SVC_H */ diff -u --recursive --new-file v2.1.31/linux/include/linux/sunrpc/svcauth.h linux/include/linux/sunrpc/svcauth.h --- v2.1.31/linux/include/linux/sunrpc/svcauth.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/sunrpc/svcauth.h Mon Mar 3 01:14:02 1997 @@ -0,0 +1,48 @@ +/* + * linux/include/linux/sunrpc/svcauth.h + * + * RPC server-side authentication stuff. + * + * Copyright (C) 1995, 1996 Olaf Kirch + */ + +#ifndef _LINUX_SUNRPC_SVCAUTH_H_ +#define _LINUX_SUNRPC_SVCAUTH_H_ + +#ifdef __KERNEL__ + +#include + +struct svc_cred { + u32 cr_flavor; + uid_t cr_uid; + gid_t cr_gid; + gid_t cr_groups[NGROUPS]; +}; + +struct svc_rqst; /* forward decl */ + +void svc_authenticate(struct svc_rqst *rqstp, u32 *statp, u32 *authp); +int svc_auth_register(u32 flavor, void (*)(struct svc_rqst *,u32 *,u32 *)); +void svc_auth_unregister(u32 flavor); + +#if 0 +/* + * Decoded AUTH_UNIX data. This is different from what's in the RPC lib. + */ +#define NGRPS 16 +struct authunix_parms { + u32 aup_stamp; + u32 aup_uid; + u32 aup_gid; + u32 aup_len; + u32 aup_gids[NGRPS]; +}; + +struct svc_authops * auth_getops(u32 flavor); +#endif + + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_SUNRPC_SVCAUTH_H_ */ diff -u --recursive --new-file v2.1.31/linux/include/linux/sunrpc/svcsock.h linux/include/linux/sunrpc/svcsock.h --- v2.1.31/linux/include/linux/sunrpc/svcsock.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/sunrpc/svcsock.h Fri Apr 4 11:38:32 1997 @@ -0,0 +1,58 @@ +/* + * linux/include/linux/sunrpc/svcsock.h + * + * RPC server socket I/O. + * + * Copyright (C) 1995, 1996 Olaf Kirch + */ + +#ifndef SUNRPC_SVCSOCK_H +#define SUNRPC_SVCSOCK_H + +#include + +/* + * RPC server socket. + * NOTE: First two items must be prev/next. + */ +struct svc_sock { + struct svc_sock * sk_prev; /* list of ready sockets */ + struct svc_sock * sk_next; + struct svc_sock * sk_list; /* list of all sockets */ + struct socket * sk_sock; /* berkeley socket layer */ + struct sock * sk_sk; /* INET layer */ + + struct svc_serv * sk_server; /* service for this socket */ + unsigned char sk_inuse; /* use count */ + unsigned char sk_busy; /* enqueued/receiving */ + unsigned char sk_conn; /* conn pending */ + unsigned char sk_close; /* dead or dying */ + int sk_data; /* data pending */ + unsigned int sk_temp : 1, /* temp socket */ + sk_qued : 1, /* on serv->sk_sockets */ + sk_dead : 1; /* socket closed */ + int (*sk_recvfrom)(struct svc_rqst *rqstp); + int (*sk_sendto)(struct svc_rqst *rqstp); + + /* We keep the old state_change and data_ready CB's here */ + void (*sk_ostate)(struct sock *); + void (*sk_odata)(struct sock *, int bytes); + + /* private TCP part */ + unsigned long sk_reclen; /* length of record */ + int sk_tcplen; /* current read length */ + + /* Debugging */ + struct svc_rqst * sk_rqstp; +}; + +/* + * Function prototypes. + */ +int svc_create_socket(struct svc_serv *, int, struct sockaddr_in *); +void svc_delete_socket(struct svc_sock *); +int svc_recv(struct svc_serv *, struct svc_rqst *); +int svc_send(struct svc_rqst *); +void svc_drop(struct svc_rqst *); + +#endif /* SUNRPC_SVCSOCK_H */ diff -u --recursive --new-file v2.1.31/linux/include/linux/sunrpc/types.h linux/include/linux/sunrpc/types.h --- v2.1.31/linux/include/linux/sunrpc/types.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/sunrpc/types.h Fri Apr 4 11:18:20 1997 @@ -0,0 +1,75 @@ +/* + * linux/include/linux/sunrpc/types.h + * + * Generic types and misc stuff for RPC. + * + * Copyright (C) 1996, Olaf Kirch + */ + +#ifndef _LINUX_SUNRPC_TYPES_H_ +#define _LINUX_SUNRPC_TYPES_H_ + +#include +#include +#include + +/* + * These are the RPC list manipulation primitives used everywhere. + */ +struct rpc_listitem { + struct rpc_listitem * prev; + struct rpc_listitem * next; +}; + +extern __inline__ void +__rpc_append_list(struct rpc_listitem **q, struct rpc_listitem *item) +{ + struct rpc_listitem *next, *prev; + + if (!(next = *q)) { + *q = item->next = item->prev = item; + } else { + prev = next->prev; + prev->next = item; + next->prev = item; + item->next = next; + item->prev = prev; + } +} + +extern __inline__ void +__rpc_insert_list(struct rpc_listitem **q, struct rpc_listitem *item) +{ + __rpc_append_list(q, item); + *q = item; +} + +extern __inline__ void +__rpc_remove_list(struct rpc_listitem **q, struct rpc_listitem *item) +{ + struct rpc_listitem *prev = item->prev, + *next = item->next; + + if (item != prev) { + next->prev = prev; + prev->next = next; + } else { + next = NULL; + } + if (*q == item) + *q = next; +} + +#define rpc_insert_list(q, i) \ + __rpc_insert_list((struct rpc_listitem **) q, (struct rpc_listitem *) i) +#define rpc_append_list(q, i) \ + __rpc_append_list((struct rpc_listitem **) q, (struct rpc_listitem *) i) +#define rpc_remove_list(q, i) \ + __rpc_remove_list((struct rpc_listitem **) q, (struct rpc_listitem *) i) + +/* + * Shorthands + */ +#define signalled() (current->signal & ~current->blocked) + +#endif /* _LINUX_SUNRPC_TYPES_H_ */ diff -u --recursive --new-file v2.1.31/linux/include/linux/sunrpc/xdr.h linux/include/linux/sunrpc/xdr.h --- v2.1.31/linux/include/linux/sunrpc/xdr.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/sunrpc/xdr.h Fri Apr 4 11:18:20 1997 @@ -0,0 +1,79 @@ +/* + * include/linux/sunrpc/xdr.h + * + * Copyright (C) 1995, 1996 Olaf Kirch + */ + +#ifndef _SUNRPC_XDR_H_ +#define _SUNRPC_XDR_H_ + +#ifdef __KERNEL__ + +#include + +/* + * Buffer adjustment + */ +#define XDR_QUADLEN(l) (((l) + 3) >> 2) + +/* + * Generic opaque `network object.' At the kernel level, this type + * is used only by lockd. + */ +#define XDR_MAX_NETOBJ 1024 +struct xdr_netobj { + unsigned int len; + u8 * data; +}; + +/* + * This is the generic XDR function. rqstp is either a rpc_rqst (client + * side) or svc_rqst pointer (server side). + * Encode functions always assume there's enough room in the buffer. + */ +typedef int (*kxdrproc_t)(void *rqstp, u32 *data, void *obj); + +/* + * These variables contain pre-xdr'ed values for faster operation. + * FIXME: should be replaced by macros for big-endian machines. + */ +extern u32 xdr_zero, xdr_one, xdr_two; + +extern u32 rpc_success, + rpc_prog_unavail, + rpc_prog_mismatch, + rpc_proc_unavail, + rpc_garbage_args, + rpc_system_err; + +extern u32 rpc_auth_ok, + rpc_autherr_badcred, + rpc_autherr_rejectedcred, + rpc_autherr_badverf, + rpc_autherr_rejectedverf, + rpc_autherr_tooweak, + rpc_autherr_dropit; + +void xdr_init(void); + +/* + * Miscellaneous XDR helper functions + */ +u32 * xdr_encode_string(u32 *p, const char *s); +u32 * xdr_decode_string(u32 *p, char **sp, int *lenp, int maxlen); +u32 * xdr_encode_netobj(u32 *p, const struct xdr_netobj *); +u32 * xdr_decode_netobj(u32 *p, struct xdr_netobj *); +u32 * xdr_decode_netobj_fixed(u32 *p, void *obj, unsigned int len); + +/* + * Adjust iovec to reflect end of xdr'ed data (RPC client XDR) + */ +static inline int +xdr_adjust_iovec(struct iovec *iov, u32 *p) +{ + return iov->iov_len = ((u8 *) p - (u8 *) iov->iov_base); +} + +#endif /* __KERNEL__ */ + +#endif /* _SUNRPC_XDR_H_ */ diff -u --recursive --new-file v2.1.31/linux/include/linux/sunrpc/xprt.h linux/include/linux/sunrpc/xprt.h --- v2.1.31/linux/include/linux/sunrpc/xprt.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/sunrpc/xprt.h Fri Apr 4 12:19:02 1997 @@ -0,0 +1,195 @@ +/* + * linux/include/linux/sunrpc/clnt_xprt.h + * + * Declarations for the RPC transport interface. + * + * Copyright (C) 1995, 1996 Olaf Kirch + */ + +#ifndef _LINUX_SUNRPC_XPRT_H +#define _LINUX_SUNRPC_XPRT_H + +#include +#include +#include +#include + +/* + * Maximum number of iov's we use. + */ +#define MAX_IOVEC 8 + +/* + * The transport code maintains an estimate on the maximum number of out- + * standing RPC requests, using a smoothed version of the congestion + * avoidance implemented in 44BSD. This is basically the Van Jacobson + * slow start algorithm: If a retransmit occurs, the congestion window is + * halved; otherwise, it is incremented by 1/cwnd when + * + * - a reply is received and + * - a full number of requests are outstanding and + * - the congestion window hasn't been updated recently. + * + * Upper procedures may check whether a request would block waiting for + * a free RPC slot by using the RPC_CONGESTED() macro. + * + * Note: on machines with low memory we should probably use a smaller + * MAXREQS value: At 32 outstanding reqs with 8 megs of RAM, fragment + * reassembly will frequently run out of memory. + * Come Linux 2.1, we'll handle fragments directly. + */ +#define RPC_MAXCONG 16 +#define RPC_MAXREQS (RPC_MAXCONG + 1) +#define RPC_CWNDSCALE 256 +#define RPC_MAXCWND (RPC_MAXCONG * RPC_CWNDSCALE) +#define RPC_INITCWND RPC_CWNDSCALE +#define RPCXPRT_CONGESTED(xprt) \ + ((xprt)->cong >= ((xprt)->nocong? RPC_MAXCWND : (xprt)->cwnd)) + +/* Default timeout values */ +#define RPC_MAX_UDP_TIMEOUT (6*HZ) +#define RPC_MAX_TCP_TIMEOUT (600*HZ) + +/* RPC call and reply header size as number of 32bit words (verifier + * size computed separately) + */ +#define RPC_CALLHDRSIZE 6 +#define RPC_REPHDRSIZE 4 + +/* + * This describes a timeout strategy + */ +struct rpc_timeout { + unsigned long to_current, /* current timeout */ + to_initval, /* initial timeout */ + to_maxval, /* max timeout */ + to_increment, /* if !exponential */ + to_resrvval; /* reserve timeout */ + short to_retries; /* max # of retries */ + unsigned char to_exponential; +}; + +/* + * This is the RPC buffer + */ +struct rpc_iov { + struct iovec io_vec[MAX_IOVEC]; + unsigned int io_nr; + unsigned int io_len; +}; + +/* + * This describes a complete RPC request + */ +struct rpc_rqst { + /* + * This is the user-visible part + */ + struct rpc_xprt * rq_xprt; /* RPC client */ + struct rpc_timeout rq_timeout; /* timeout parms */ + struct rpc_iov rq_snd_buf; /* send buffer */ + struct rpc_iov rq_rcv_buf; /* recv buffer */ + + /* + * This is the private part + */ + struct rpc_task * rq_task; /* RPC task data */ + __u32 rq_xid; /* request XID */ + struct rpc_rqst * rq_next; /* free list */ + unsigned char rq_gotit; /* reply received */ + unsigned char rq_damaged; /* being received */ + + /* + * For authentication (e.g. auth_des) + */ + u32 rq_creddata[2]; + +#ifdef RPC_PROFILE + unsigned long rq_xtime; /* when transmitted */ +#endif +}; +#define rq_svec rq_snd_buf.io_vec +#define rq_snr rq_snd_buf.io_nr +#define rq_slen rq_snd_buf.io_len +#define rq_rvec rq_rcv_buf.io_vec +#define rq_rnr rq_rcv_buf.io_nr +#define rq_rlen rq_rcv_buf.io_len + +struct rpc_xprt { + struct rpc_xprt * link; /* list of all clients */ + + struct file * file; /* VFS layer */ + struct socket * sock; /* BSD socket layer */ + struct sock * inet; /* INET layer */ + + struct rpc_timeout timeout; /* timeout parms */ + struct sockaddr_in addr; /* server address */ + int prot; /* IP protocol */ + + unsigned long cong; /* current congestion */ + unsigned long cwnd; /* congestion window */ + unsigned long congtime; /* hold cwnd until then */ + + struct rpc_wait_queue sending; /* requests waiting to send */ + struct rpc_wait_queue pending; /* requests in flight */ + struct rpc_wait_queue backlog; /* waiting for slot */ + struct rpc_wait_queue reconn; /* waiting for reconnect */ + struct rpc_rqst * free; /* free slots */ + struct rpc_rqst slot[RPC_MAXREQS]; + unsigned char connected; /* TCP: connected */ + unsigned char write_space; /* TCP: can send */ + unsigned int shutdown : 1, /* being shut down */ + nocong : 1, /* no congestion control */ + stream : 1, /* TCP */ + tcp_more : 1, /* more record fragments */ + connecting : 1; /* being reconnected */ + + /* + * State of TCP reply receive stuff + */ + union { /* record marker & XID */ + u32 header[2]; + u8 * data[8]; + } tcp_recm; + struct rpc_rqst * tcp_rqstp; + struct iovec tcp_iovec[MAX_IOVEC]; + u32 tcp_total; /* overall record length */ + u32 tcp_reclen; /* fragment length */ + u32 tcp_offset; /* fragment offset */ + u32 tcp_copied; /* copied to request */ + + /* + * TCP send stuff + */ + struct rpc_iov snd_buf; /* send buffer */ + struct rpc_task * snd_task; /* Task blocked in send */ + + void (*old_data_ready)(struct sock *, int); + void (*old_state_change)(struct sock *); + void (*old_write_space)(struct sock *); +}; +#define tcp_reclen tcp_recm.header[0] +#define tcp_xid tcp_recm.header[1] + +#ifdef __KERNEL__ + +struct rpc_xprt * xprt_create(struct file *socket, + struct sockaddr_in *addr, + struct rpc_timeout *toparms); +struct rpc_xprt * xprt_create_proto(int proto, struct sockaddr_in *addr, + struct rpc_timeout *toparms); +int xprt_destroy(struct rpc_xprt *); +void xprt_default_timeout(struct rpc_timeout *, int); +void xprt_set_timeout(struct rpc_timeout *, unsigned int, + unsigned long); + +int xprt_reserve(struct rpc_task *); +void xprt_transmit(struct rpc_task *); +void xprt_receive(struct rpc_task *); +int xprt_adjust_timeout(struct rpc_timeout *); +void xprt_release(struct rpc_task *); +void xprt_reconnect(struct rpc_task *); + +#endif /* __KERNEL__*/ + +#endif /* _LINUX_SUNRPC_XPRT_H */ diff -u --recursive --new-file v2.1.31/linux/include/linux/sysctl.h linux/include/linux/sysctl.h --- v2.1.31/linux/include/linux/sysctl.h Thu Mar 27 14:40:11 1997 +++ linux/include/linux/sysctl.h Sat Apr 5 12:14:20 1997 @@ -28,64 +28,80 @@ /* For internal pattern-matching use only: */ #ifdef __KERNEL__ #define CTL_ANY -1 /* Matches any name */ -#define CTL_NONE 0 +#define CTL_NONE 0 #endif -#define CTL_KERN 1 /* General kernel info and control */ -#define CTL_VM 2 /* VM management */ -#define CTL_NET 3 /* Networking */ -#define CTL_PROC 4 /* Process info */ -#define CTL_FS 5 /* Filesystems */ -#define CTL_DEBUG 6 /* Debugging */ -#define CTL_DEV 7 /* Devices */ -#define CTL_MAXID 8 +enum +{ + CTL_KERN=1, /* General kernel info and control */ + CTL_VM, /* VM management */ + CTL_NET, /* Networking */ + CTL_PROC, /* Process info */ + CTL_FS, /* Filesystems */ + CTL_DEBUG, /* Debugging */ + CTL_DEV, /* Devices */ +}; + /* CTL_KERN names: */ -#define KERN_OSTYPE 1 /* string: system version */ -#define KERN_OSRELEASE 2 /* string: system release */ -#define KERN_OSREV 3 /* int: system revision */ -#define KERN_VERSION 4 /* string: compile time info */ -#define KERN_SECUREMASK 5 /* struct: maximum rights mask */ -#define KERN_PROF 6 /* table: profiling information */ -#define KERN_NODENAME 7 -#define KERN_DOMAINNAME 8 -#define KERN_NRINODE 9 -#define KERN_MAXINODE 10 -#define KERN_NRFILE 11 -#define KERN_MAXFILE 12 -#define KERN_MAXID 13 -#define KERN_SECURELVL 14 /* int: system security level */ -#define KERN_PANIC 15 /* int: panic timeout */ -#define KERN_REALROOTDEV 16 /* real root device to mount after initrd */ -#define KERN_NFSRNAME 17 /* NFS root name */ -#define KERN_NFSRADDRS 18 /* NFS root addresses */ -#define KERN_JAVA_INTERPRETER 19 /* path to Java(tm) interpreter */ -#define KERN_JAVA_APPLETVIEWER 20 /* path to Java(tm) appletviewer */ -#define KERN_SPARC_REBOOT 21 /* reboot command on Sparc */ +enum +{ + KERN_OSTYPE=1, /* string: system version */ + KERN_OSRELEASE, /* string: system release */ + KERN_OSREV, /* int: system revision */ + KERN_VERSION, /* string: compile time info */ + KERN_SECUREMASK, /* struct: maximum rights mask */ + KERN_PROF, /* table: profiling information */ + KERN_NODENAME, + KERN_DOMAINNAME, + KERN_NRINODE, + KERN_MAXINODE, + KERN_NRFILE, + KERN_MAXFILE, + KERN_SECURELVL, /* int: system security level */ + KERN_PANIC, /* int: panic timeout */ + KERN_REALROOTDEV, /* real root device to mount after initrd */ + KERN_NFSRNAME, /* NFS root name */ + KERN_NFSRADDRS, /* NFS root addresses */ + KERN_JAVA_INTERPRETER, /* path to Java(tm) interpreter */ + KERN_JAVA_APPLETVIEWER, /* path to Java(tm) appletviewer */ + KERN_SPARC_REBOOT, /* reboot command on Sparc */ + KERN_CTLALTDEL, /* int: allow ctl-alt-del to reboot */ + KERN_PRINTK, /* sturct: control printk logging parameters */ +}; + /* CTL_VM names: */ -#define VM_SWAPCTL 1 /* struct: Set vm swapping control */ -#define VM_KSWAPD 2 /* struct: control background pageout */ -#define VM_FREEPG 3 /* struct: Set free page thresholds */ -#define VM_BDFLUSH 4 /* struct: Control buffer cache flushing */ -#define VM_MAXID 5 -#define VM_OVERCOMMIT_MEMORY 7 /* Turn off the virtual memory safety limit */ +enum +{ + VM_SWAPCTL=1, /* struct: Set vm swapping control */ + VM_KSWAPD, /* struct: control background pageout */ + VM_SWAPOUT, /* int: Background pageout interval */ + VM_FREEPG, /* struct: Set free page thresholds */ + VM_BDFLUSH, /* struct: Control buffer cache flushing */ + VM_OVERCOMMIT_MEMORY, /* Turn off the virtual memory safety limit */ +}; + /* CTL_NET names: */ -#define NET_CORE 1 -#define NET_ETHER 2 -#define NET_802 3 -#define NET_UNIX 4 -#define NET_IPV4 5 -#define NET_IPX 6 -#define NET_ATALK 7 -#define NET_NETROM 8 -#define NET_AX25 9 -#define NET_BRIDGE 10 -#define NET_IPV6 11 -#define NET_ROSE 12 -#define NET_X25 13 -#define NET_TR 14 +enum +{ + NET_CORE=1, + NET_ETHER, + NET_802, + NET_UNIX, + NET_IPV4, + NET_IPX, + NET_ATALK, + NET_NETROM, + NET_AX25, + NET_BRIDGE, + NET_IPV6, + NET_ROSE, + NET_X25, + NET_TR, +}; + /* /proc/sys/net/core */ @@ -218,7 +234,7 @@ #ifdef __KERNEL__ extern asmlinkage int sys_sysctl(struct __sysctl_args *); -extern void init_sysctl(void); +extern void sysctl_init(void); typedef struct ctl_table ctl_table; diff -u --recursive --new-file v2.1.31/linux/init/main.c linux/init/main.c --- v2.1.31/linux/init/main.c Fri Apr 4 08:52:26 1997 +++ linux/init/main.c Sat Apr 5 12:14:20 1997 @@ -909,7 +909,9 @@ #ifdef __SMP__ smp_init(); #endif +#ifdef CONFIG_SYSCTL sysctl_init(); +#endif /* * We count on the initial thread going ok * Like idlers init is an unlocked kernel thread, which will diff -u --recursive --new-file v2.1.31/linux/kernel/ksyms.c linux/kernel/ksyms.c --- v2.1.31/linux/kernel/ksyms.c Fri Apr 4 08:52:26 1997 +++ linux/kernel/ksyms.c Thu Apr 3 10:23:56 1997 @@ -150,6 +150,7 @@ EXPORT_SYMBOL(permission); EXPORT_SYMBOL(inode_setattr); EXPORT_SYMBOL(inode_change_ok); +EXPORT_SYMBOL(get_hardblocksize); EXPORT_SYMBOL(set_blocksize); EXPORT_SYMBOL(getblk); EXPORT_SYMBOL(bread); diff -u --recursive --new-file v2.1.31/linux/kernel/printk.c linux/kernel/printk.c --- v2.1.31/linux/kernel/printk.c Fri Apr 4 08:52:26 1997 +++ linux/kernel/printk.c Sat Apr 5 12:14:20 1997 @@ -9,6 +9,7 @@ * to the console. Added hook for sending the console messages * elsewhere, in preparation for a serial line console (someday). * Ted Ts'o, 2/11/93. + * Modified for sysctl support, 1/8/97, Chris Horn. */ #include @@ -40,7 +41,12 @@ unsigned long log_size = 0; struct wait_queue * log_wait = NULL; + +/* Keep together for sysctl support */ int console_loglevel = DEFAULT_CONSOLE_LOGLEVEL; +int default_message_loglevel = DEFAULT_MESSAGE_LOGLEVEL; +int minimum_console_loglevel = MINIMUM_CONSOLE_LOGLEVEL; +int default_console_loglevel = DEFAULT_CONSOLE_LOGLEVEL; struct console *console_drivers = NULL; static char log_buf[LOG_BUF_LEN]; @@ -141,17 +147,17 @@ logged_chars = 0; break; case 6: /* Disable logging to console */ - console_loglevel = MINIMUM_CONSOLE_LOGLEVEL; + console_loglevel = minimum_console_loglevel; break; case 7: /* Enable logging to console */ - console_loglevel = DEFAULT_CONSOLE_LOGLEVEL; + console_loglevel = default_console_loglevel; break; case 8: error = -EINVAL; if (len < 1 || len > 8) goto out; - if (len < MINIMUM_CONSOLE_LOGLEVEL) - len = MINIMUM_CONSOLE_LOGLEVEL; + if (len < minimum_console_loglevel) + len = minimum_console_loglevel; console_loglevel = len; error = 0; break; @@ -190,7 +196,7 @@ ) { p -= 3; p[0] = '<'; - p[1] = DEFAULT_MESSAGE_LOGLEVEL + '0'; + p[1] = default_message_loglevel + '0'; p[2] = '>'; } else msg += 3; diff -u --recursive --new-file v2.1.31/linux/kernel/sys.c linux/kernel/sys.c --- v2.1.31/linux/kernel/sys.c Fri Apr 4 08:52:26 1997 +++ linux/kernel/sys.c Fri Apr 4 08:37:59 1997 @@ -893,13 +893,14 @@ gid_t *groups = current->groups; do { if (*groups == grp) - break; + goto out; groups++; i--; } while (i); } return 0; } +out: return 1; } @@ -1138,7 +1139,6 @@ asmlinkage int sys_umask(int mask) { - /* The xchg() isn't SMP-safe on x86 right now.. */ mask = xchg(¤t->fs->umask, mask & S_IRWXUGO); return mask; } diff -u --recursive --new-file v2.1.31/linux/kernel/sysctl.c linux/kernel/sysctl.c --- v2.1.31/linux/kernel/sysctl.c Fri Apr 4 08:52:26 1997 +++ linux/kernel/sysctl.c Sat Apr 5 12:14:20 1997 @@ -7,6 +7,8 @@ * Added hooks for /proc/sys/net (minor, minor patch), 96/4/1, Mike Shaver. * Added kernel/java-{interpreter,appletviewer}, 96/5/10, Mike Shaver. * Dynamic registration fixes, Stephen Tweedie. + * Added kswapd-interval, ctrl-alt-del, printk stuff, 1/8/97, Chris Horn. + * Made sysctl support optional via CONFIG_SYSCTL, 1/10/97, Chris Horn. */ #include @@ -26,24 +28,43 @@ #include #include +#ifdef CONFIG_ROOT_NFS +#include +#endif + +#ifdef CONFIG_SYSCTL + /* External variables not in a header file. */ extern int panic_timeout; +extern int console_loglevel, default_message_loglevel; +extern int minimum_console_loglevel, default_console_loglevel; +extern int C_A_D, swapout_interval; +extern int bdf_prm[], bdflush_min[], bdflush_max[]; +extern char binfmt_java_interpreter[], binfmt_java_appletviewer[]; +extern int sysctl_overcommit_memory; - -#ifdef CONFIG_ROOT_NFS -#include +#ifdef __sparc__ +extern char reboot_command []; #endif +static int parse_table(int *, int, void *, size_t *, void *, size_t, + ctl_table *, void **); +static int do_securelevel_strategy (ctl_table *, int *, int, void *, size_t *, + void *, size_t, void **); + + static ctl_table root_table[]; static struct ctl_table_header root_table_header = {root_table, DNODE_SINGLE(&root_table_header)}; -static int parse_table(int *, int, void *, size_t *, void *, size_t, - ctl_table *, void **); - static ctl_table kern_table[]; static ctl_table vm_table[]; extern ctl_table net_table[]; +static ctl_table proc_table[]; +static ctl_table fs_table[]; +static ctl_table debug_table[]; +static ctl_table dev_table[]; + /* /proc declarations: */ @@ -96,24 +117,16 @@ static void unregister_proc_table(ctl_table *, struct proc_dir_entry *); #endif -extern int bdf_prm[], bdflush_min[], bdflush_max[]; - -static int do_securelevel_strategy (ctl_table *, int *, int, void *, size_t *, - void *, size_t, void **); - -extern char binfmt_java_interpreter[], binfmt_java_appletviewer[]; -extern int sysctl_overcommit_memory; - -#ifdef __sparc__ -extern char reboot_command []; -#endif - /* The default sysctl tables: */ static ctl_table root_table[] = { {CTL_KERN, "kernel", NULL, 0, 0555, kern_table}, {CTL_VM, "vm", NULL, 0, 0555, vm_table}, {CTL_NET, "net", NULL, 0, 0555, net_table}, + {CTL_PROC, "proc", NULL, 0, 0555, proc_table}, + {CTL_FS, "fs", NULL, 0, 0555, fs_table}, + {CTL_DEBUG, "debug", NULL, 0, 0555, debug_table}, + {CTL_DEV, "dev", NULL, 0, 0555, dev_table}, {0} }; @@ -160,6 +173,10 @@ {KERN_SPARC_REBOOT, "reboot-cmd", reboot_command, 256, 0644, NULL, &proc_dostring, &sysctl_string }, #endif + {KERN_CTLALTDEL, "ctrl-alt-del", &C_A_D, sizeof(int), + 0644, NULL, &proc_dointvec}, + {KERN_PRINTK, "printk", &console_loglevel, 4*sizeof(int), + 0644, NULL, &proc_dointvec}, {0} }; @@ -168,6 +185,8 @@ &swap_control, sizeof(swap_control_t), 0600, NULL, &proc_dointvec}, {VM_KSWAPD, "kswapd", &kswapd_ctl, sizeof(kswapd_ctl), 0600, NULL, &proc_dointvec}, + {VM_SWAPOUT, "kswapd-interval", + &swapout_interval, sizeof(int), 0600, NULL, &proc_dointvec}, {VM_FREEPG, "freepages", &min_free_pages, 3*sizeof(int), 0600, NULL, &proc_dointvec}, {VM_BDFLUSH, "bdflush", &bdf_prm, 9*sizeof(int), 0600, NULL, @@ -178,6 +197,23 @@ {0} }; +static ctl_table proc_table[] = { + {0} +}; + +static ctl_table fs_table[] = { + {0} +}; + +static ctl_table debug_table[] = { + {0} +}; + +static ctl_table dev_table[] = { + {0} +}; + + void sysctl_init(void) { #ifdef CONFIG_PROC_FS @@ -942,4 +978,59 @@ return -EFAULT; return 0; } + + +#else /* CONFIG_SYSCTL */ + + +extern asmlinkage int sys_sysctl(struct __sysctl_args *args) +{ + return -ENOSYS; +} + +int sysctl_string(ctl_table *table, int *name, int nlen, + void *oldval, size_t *oldlenp, + void *newval, size_t newlen, void **context) +{ + return -ENOSYS; +} + +int sysctl_intvec(ctl_table *table, int *name, int nlen, + void *oldval, size_t *oldlenp, + void *newval, size_t newlen, void **context) +{ + return -ENOSYS; +} + +int proc_dostring(ctl_table *table, int write, struct file *filp, + void *buffer, size_t *lenp) +{ + return -ENOSYS; +} + +int proc_dointvec(ctl_table *table, int write, struct file *filp, + void *buffer, size_t *lenp) +{ + return -ENOSYS; +} + +int proc_dointvec_minmax(ctl_table *table, int write, struct file *filp, + void *buffer, size_t *lenp) +{ + return -ENOSYS; +} + +struct ctl_table_header * register_sysctl_table(ctl_table * table, + int insert_at_head) +{ + return 0; +} + +void unregister_sysctl_table(struct ctl_table_header * table) +{ +} + +#endif /* CONFIG_SYSCTL */ + + diff -u --recursive --new-file v2.1.31/linux/net/802/Makefile linux/net/802/Makefile --- v2.1.31/linux/net/802/Makefile Sun Jan 26 02:07:50 1997 +++ linux/net/802/Makefile Sat Apr 5 12:14:21 1997 @@ -8,13 +8,16 @@ # Note 2! The CFLAGS definition is now in the main makefile... O_TARGET := 802.o -O_OBJS = p8023.o sysctl_net_802.o +O_OBJS = p8023.o + +ifeq ($(CONFIG_SYSCTL),y) +O_OBJS += sysctl_net_802.o +endif ifeq ($(CONFIG_LLC),y) SUB_DIRS += transit O_OBJS += llc_sendpdu.o llc_utility.o cl2llc.o OX_OBJS += llc_macinit.o - endif ifdef CONFIG_TR diff -u --recursive --new-file v2.1.31/linux/net/Makefile linux/net/Makefile --- v2.1.31/linux/net/Makefile Sun Feb 2 05:18:48 1997 +++ linux/net/Makefile Sat Apr 5 12:14:21 1997 @@ -9,7 +9,7 @@ MOD_SUB_DIRS := ipv4 ALL_SUB_DIRS := 802 ax25 bridge core ethernet ipv4 ipv6 ipx unix appletalk \ - netrom rose lapb x25 wanrouter #decnet + netrom rose lapb x25 wanrouter sunrpc #decnet SUB_DIRS := core ethernet unix MOD_LIST_NAME := NET_MISC_MODULES @@ -97,6 +97,14 @@ endif endif +ifeq ($(CONFIG_SUNRPC),y) +SUB_DIRS += sunrpc +else + ifeq ($(CONFIG_SUNRPC),m) + MOD_SUB_DIRS += sunrpc + endif +endif + # We must attach netsyms.o to socket.o, as otherwise there is nothing # to pull the object file from the archive. @@ -111,9 +119,13 @@ endif L_TARGET := network.a -L_OBJS := $(SOCK) protocols.o sysctl_net.o $(join $(SUB_DIRS),$(SUB_DIRS:%=/%.o)) +L_OBJS := $(SOCK) protocols.o $(join $(SUB_DIRS),$(SUB_DIRS:%=/%.o)) M_OBJS := + +ifeq ($(CONFIG_SYSCTL),y) +L_OBJS += sysctl_net.o +endif CONFIG_NETLINK_BUILTIN := CONFIG_NETLINK_MODULE := diff -u --recursive --new-file v2.1.31/linux/net/appletalk/Makefile linux/net/appletalk/Makefile --- v2.1.31/linux/net/appletalk/Makefile Mon Apr 1 21:43:08 1996 +++ linux/net/appletalk/Makefile Sat Apr 5 12:14:21 1997 @@ -8,8 +8,12 @@ # Note 2! The CFLAGS definition is now in the main makefile... O_TARGET := appletalk.o -O_OBJS := aarp.o ddp.o sysctl_net_atalk.o +O_OBJS := aarp.o ddp.o M_OBJS := $(O_TARGET) + +ifeq ($(CONFIG_SYSCTL),y) +O_OBJS += sysctl_net_atalk.o +endif include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.1.31/linux/net/ax25/Makefile linux/net/ax25/Makefile --- v2.1.31/linux/net/ax25/Makefile Thu Mar 27 14:40:13 1997 +++ linux/net/ax25/Makefile Sat Apr 5 12:14:21 1997 @@ -11,13 +11,17 @@ O_TARGET := ax25.o O_OBJS := ax25_addr.o ax25_dev.o ax25_iface.o ax25_in.o ax25_ip.o ax25_out.o \ ax25_route.o ax25_std_in.o ax25_std_subr.o ax25_std_timer.o \ - ax25_subr.o ax25_timer.o ax25_uid.o sysctl_net_ax25.o + ax25_subr.o ax25_timer.o ax25_uid.o M_OBJS := $(O_TARGET) OX_OBJS += af_ax25.o ifeq ($(CONFIG_AX25_DAMA_SLAVE),y) O_OBJS += ax25_ds_in.o ax25_ds_subr.o ax25_ds_timer.o +endif + +ifeq ($(CONFIG_SYSCTL),y) +O_OBJS += sysctl_net_ax25.o endif include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.1.31/linux/net/ax25/af_ax25.c linux/net/ax25/af_ax25.c --- v2.1.31/linux/net/ax25/af_ax25.c Thu Mar 27 14:40:13 1997 +++ linux/net/ax25/af_ax25.c Sat Apr 5 12:14:21 1997 @@ -1797,7 +1797,9 @@ ax25_packet_type.type = htons(ETH_P_AX25); dev_add_pack(&ax25_packet_type); register_netdevice_notifier(&ax25_dev_notifier); +#ifdef CONFIG_SYSCTL ax25_register_sysctl(); +#endif #ifdef CONFIG_PROC_FS proc_net_register(&proc_ax25_route); @@ -1828,8 +1830,9 @@ ax25_uid_free(); ax25_dev_free(); +#ifdef CONFIG_SYSCTL ax25_unregister_sysctl(); - +#endif unregister_netdevice_notifier(&ax25_dev_notifier); ax25_packet_type.type = htons(ETH_P_AX25); diff -u --recursive --new-file v2.1.31/linux/net/ax25/ax25_dev.c linux/net/ax25/ax25_dev.c --- v2.1.31/linux/net/ax25/ax25_dev.c Thu Mar 27 14:40:13 1997 +++ linux/net/ax25/ax25_dev.c Sat Apr 5 12:14:21 1997 @@ -81,7 +81,9 @@ return; } +#ifdef CONFIG_SYSCTL ax25_unregister_sysctl(); +#endif memset(ax25_dev, 0x00, sizeof(*ax25_dev)); @@ -112,7 +114,9 @@ ax25_dev_list = ax25_dev; restore_flags(flags); +#ifdef CONFIG_SYSCTL ax25_register_sysctl(); +#endif } void ax25_dev_device_down(struct device *dev) @@ -123,7 +127,9 @@ if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) return; +#ifdef CONFIG_SYSCTL ax25_unregister_sysctl(); +#endif save_flags(flags); cli(); @@ -142,7 +148,9 @@ ax25_dev_list = s->next; restore_flags(flags); kfree_s(ax25_dev, sizeof(ax25_dev)); +#ifdef CONFIG_SYSCTL ax25_register_sysctl(); +#endif return; } @@ -151,7 +159,9 @@ s->next = ax25_dev->next; restore_flags(flags); kfree_s(ax25_dev, sizeof(ax25_dev)); +#ifdef CONFIG_SYSCTL ax25_register_sysctl(); +#endif return; } @@ -159,7 +169,9 @@ } restore_flags(flags); +#ifdef CONFIG_SYSCTL ax25_register_sysctl(); +#endif } int ax25_fwd_ioctl(unsigned int cmd, struct ax25_fwd_struct *fwd) diff -u --recursive --new-file v2.1.31/linux/net/bridge/Makefile linux/net/bridge/Makefile --- v2.1.31/linux/net/bridge/Makefile Mon Jun 3 02:42:41 1996 +++ linux/net/bridge/Makefile Sat Apr 5 12:14:21 1997 @@ -8,8 +8,12 @@ # Note 2! The CFLAGS definition is now in the main makefile... O_TARGET := bridge.o -O_OBJS := br.o br_tree.o sysctl_net_bridge.o +O_OBJS := br.o br_tree.o M_OBJS := $(O_TARGET) + +ifeq ($(CONFIG_SYSCTL),y) +O_OBJS += sysctl_net_bridge.o +endif include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.1.31/linux/net/core/Makefile linux/net/core/Makefile --- v2.1.31/linux/net/core/Makefile Thu Dec 12 06:54:23 1996 +++ linux/net/core/Makefile Sat Apr 5 12:14:21 1997 @@ -9,8 +9,12 @@ O_TARGET := core.o -O_OBJS := sock.o skbuff.o iovec.o datagram.o sysctl_net_core.o dst.o scm.o \ +O_OBJS := sock.o skbuff.o iovec.o datagram.o dst.o scm.o \ neighbour.o + +ifeq ($(CONFIG_SYSCTL),y) +O_OBJS += sysctl_net_core.o +endif ifdef CONFIG_NET diff -u --recursive --new-file v2.1.31/linux/net/ethernet/Makefile linux/net/ethernet/Makefile --- v2.1.31/linux/net/ethernet/Makefile Mon Apr 1 21:43:08 1996 +++ linux/net/ethernet/Makefile Sat Apr 5 12:14:21 1997 @@ -9,7 +9,11 @@ O_TARGET := ethernet.o -OBJS := eth.o sysctl_net_ether.o +OBJS := eth.o + +ifeq ($(CONFIG_SYSCTL),y) +OBJS += sysctl_net_ether.o +endif ifdef CONFIG_IPX OBJ2 := pe2.o diff -u --recursive --new-file v2.1.31/linux/net/ipv4/sysctl_net_ipv4.c linux/net/ipv4/sysctl_net_ipv4.c --- v2.1.31/linux/net/ipv4/sysctl_net_ipv4.c Thu Dec 12 06:54:24 1996 +++ linux/net/ipv4/sysctl_net_ipv4.c Sat Apr 5 12:14:21 1997 @@ -39,6 +39,9 @@ void *buffer, size_t *lenp); struct ipv4_config ipv4_config = { 1, 1, 1, 1, }; + +#ifdef CONFIG_SYSCTL + struct ipv4_config ipv4_def_router_config = { 0, 1, 1, 1, 1, 1, 1, }; struct ipv4_config ipv4_def_host_config = { 1, 1, 1, 1, }; @@ -121,3 +124,5 @@ &proc_dointvec}, {0} }; + +#endif /* CONFIG_SYSCTL */ diff -u --recursive --new-file v2.1.31/linux/net/ipv6/ipv6_sockglue.c linux/net/ipv6/ipv6_sockglue.c --- v2.1.31/linux/net/ipv6/ipv6_sockglue.c Thu Mar 27 14:40:16 1997 +++ linux/net/ipv6/ipv6_sockglue.c Sat Apr 5 12:14:21 1997 @@ -228,7 +228,7 @@ return 0; } -#ifdef MODULE +#if defined(MODULE) && defined(CONFIG_SYSCTL) /* * sysctl registration functions defined in sysctl_net_ipv6.c @@ -244,7 +244,7 @@ dev_add_pack(&ipv6_packet_type); -#ifdef MODULE +#if defined(MODULE) && defined(CONFIG_SYSCTL) ipv6_sysctl_register(); #endif @@ -258,9 +258,13 @@ { unregister_netdevice_notifier(&ipv6_dev_notf); dev_remove_pack(&ipv6_packet_type); +#ifdef CONFIG_SYSCTL ipv6_sysctl_unregister(); +#endif ip6_route_cleanup(); ndisc_cleanup(); addrconf_cleanup(); } #endif + + diff -u --recursive --new-file v2.1.31/linux/net/ipv6/sysctl_net_ipv6.c linux/net/ipv6/sysctl_net_ipv6.c --- v2.1.31/linux/net/ipv6/sysctl_net_ipv6.c Thu Mar 27 14:40:17 1997 +++ linux/net/ipv6/sysctl_net_ipv6.c Sat Apr 5 12:14:21 1997 @@ -33,6 +33,8 @@ 30*HZ, /* rt gc period */ }; +#ifdef CONFIG_SYSCTL + int ipv6_sysctl_forwarding(ctl_table *ctl, int write, struct file * filp, void *buffer, size_t *lenp) { @@ -140,3 +142,8 @@ unregister_sysctl_table(ipv6_sysctl_header); } #endif /* MODULE */ + +#endif /* CONFIG_SYSCTL */ + + + diff -u --recursive --new-file v2.1.31/linux/net/ipx/Makefile linux/net/ipx/Makefile --- v2.1.31/linux/net/ipx/Makefile Mon Apr 1 21:43:08 1996 +++ linux/net/ipx/Makefile Sat Apr 5 12:14:21 1997 @@ -8,8 +8,12 @@ # Note 2! The CFLAGS definition is now in the main makefile... O_TARGET := ipx.o -O_OBJS := af_ipx.o sysctl_net_ipx.o +O_OBJS := af_ipx.o M_OBJS := $(O_TARGET) + +ifeq ($(CONFIG_SYSCTL),y) +O_OBJS += sysctl_net_ipx.o +endif include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.1.31/linux/net/netrom/Makefile linux/net/netrom/Makefile --- v2.1.31/linux/net/netrom/Makefile Sun Nov 10 09:12:58 1996 +++ linux/net/netrom/Makefile Sat Apr 5 12:14:21 1997 @@ -8,8 +8,12 @@ # Note 2! The CFLAGS definition is now in the main makefile... O_TARGET := netrom.o -O_OBJS := af_netrom.o sysctl_net_netrom.o nr_dev.o nr_in.o nr_out.o nr_route.o nr_subr.o nr_timer.o +O_OBJS := af_netrom.o nr_dev.o nr_in.o nr_out.o nr_route.o nr_subr.o nr_timer.o M_OBJS := $(O_TARGET) + +ifeq ($(CONFIG_SYSCTL),y) +O_OBJS += sysctl_net_netrom.o +endif include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.1.31/linux/net/netrom/af_netrom.c linux/net/netrom/af_netrom.c --- v2.1.31/linux/net/netrom/af_netrom.c Thu Mar 27 14:40:17 1997 +++ linux/net/netrom/af_netrom.c Sat Apr 5 12:14:21 1997 @@ -1376,7 +1376,9 @@ for (i = 0; i < 4; i++) register_netdev(&dev_nr[i]); +#ifdef CONFIG_SYSCTL nr_register_sysctl(); +#endif #ifdef CONFIG_PROC_FS proc_net_register(&proc_net_nr); @@ -1411,8 +1413,9 @@ unregister_netdevice_notifier(&nr_dev_notifier); +#ifdef CONFIG_SYSCTL nr_unregister_sysctl(); - +#endif sock_unregister(AF_NETROM); for (i = 0; i < 4; i++) { diff -u --recursive --new-file v2.1.31/linux/net/netsyms.c linux/net/netsyms.c --- v2.1.31/linux/net/netsyms.c Fri Apr 4 08:52:29 1997 +++ linux/net/netsyms.c Thu Apr 3 19:16:54 1997 @@ -150,10 +150,6 @@ EXPORT_SYMBOL(scm_detach_fds); #endif -#ifdef CONFIG_SMB_FS_MODULE -EXPORT_SYMBOL(scm_detach_fds); -#endif - #ifdef CONFIG_INET /* Internet layer registration */ EXPORT_SYMBOL(inet_add_protocol); @@ -264,7 +260,6 @@ EXPORT_SYMBOL(register_trdev); EXPORT_SYMBOL(unregister_trdev); EXPORT_SYMBOL(init_trdev); -EXPORT_SYMBOL(csum_partial_copy); #endif #ifdef CONFIG_NET_ALIAS diff -u --recursive --new-file v2.1.31/linux/net/rose/Makefile linux/net/rose/Makefile --- v2.1.31/linux/net/rose/Makefile Sun Nov 10 09:12:58 1996 +++ linux/net/rose/Makefile Sat Apr 5 12:14:21 1997 @@ -8,8 +8,12 @@ # Note 2! The CFLAGS definition is now in the main makefile... O_TARGET := rose.o -O_OBJS := af_rose.o sysctl_net_rose.o rose_dev.o rose_in.o rose_link.o rose_out.o rose_route.o rose_subr.o rose_timer.o +O_OBJS := af_rose.o rose_dev.o rose_in.o rose_link.o rose_out.o rose_route.o rose_subr.o rose_timer.o M_OBJS := $(O_TARGET) + +ifeq ($(CONFIG_SYSCTL),y) +O_OBJS += sysctl_net_rose.o +endif include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.1.31/linux/net/rose/af_rose.c linux/net/rose/af_rose.c --- v2.1.31/linux/net/rose/af_rose.c Thu Mar 27 14:40:17 1997 +++ linux/net/rose/af_rose.c Sat Apr 5 12:14:21 1997 @@ -1398,7 +1398,9 @@ for (i = 0; i < 6; i++) register_netdev(&dev_rose[i]); +#ifdef CONFIG_SYSCTL rose_register_sysctl(); +#endif #ifdef CONFIG_PROC_FS proc_net_register(&proc_net_rose); @@ -1436,8 +1438,9 @@ if (ax25cmp(&rose_callsign, &null_ax25_address) != 0) ax25_listen_release(&rose_callsign, NULL); +#ifdef CONFIG_SYSCTL rose_unregister_sysctl(); - +#endif unregister_netdevice_notifier(&rose_dev_notifier); sock_unregister(AF_ROSE); diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/Makefile linux/net/sunrpc/Makefile --- v2.1.31/linux/net/sunrpc/Makefile Wed Dec 31 16:00:00 1969 +++ linux/net/sunrpc/Makefile Sat Mar 1 13:41:10 1997 @@ -0,0 +1,23 @@ +# +# Makefile for Linux kernel SUN RPC +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now in the main makefile... + +O_TARGET := sunrpc.o +O_OBJS := clnt.o xprt.o sched.o \ + auth.o auth_null.o auth_unix.o \ + svc.o svcsock.o svcauth.o \ + pmap_clnt.o xdr.o sysctl.o +OX_OBJS := sunrpc_syms.o + +ifeq ($(CONFIG_PROC_FS),y) + O_OBJS += stats.o +endif + +M_OBJS := $(O_TARGET) + +include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/auth.c linux/net/sunrpc/auth.c --- v2.1.31/linux/net/sunrpc/auth.c Wed Dec 31 16:00:00 1969 +++ linux/net/sunrpc/auth.c Fri Dec 13 06:53:18 1996 @@ -0,0 +1,295 @@ +/* + * linux/fs/nfs/rpcauth.c + * + * Generic RPC authentication API. + * + * Copyright (C) 1996, Olaf Kirch + */ + +#include +#include +#include +#include +#include +#include + +#ifdef RPC_DEBUG +# define RPCDBG_FACILITY RPCDBG_AUTH +#endif + +#define RPC_MAXFLAVOR 8 + +static struct rpc_authops * auth_flavors[RPC_MAXFLAVOR] = { + &authnull_ops, /* AUTH_NULL */ + &authunix_ops, /* AUTH_UNIX */ + NULL, /* others can be loadable modules */ +}; + +int +rpcauth_register(struct rpc_authops *ops) +{ + unsigned int flavor; + + if ((flavor = ops->au_flavor) >= RPC_MAXFLAVOR) + return -EINVAL; + if (auth_flavors[flavor] != NULL) + return -EPERM; /* what else? */ + auth_flavors[flavor] = ops; + return 0; +} + +int +rpcauth_unregister(struct rpc_authops *ops) +{ + unsigned int flavor; + + if ((flavor = ops->au_flavor) >= RPC_MAXFLAVOR) + return -EINVAL; + if (auth_flavors[flavor] != ops) + return -EPERM; /* what else? */ + auth_flavors[flavor] = NULL; + return 0; +} + +struct rpc_auth * +rpcauth_create(unsigned int flavor, struct rpc_clnt *clnt) +{ + struct rpc_authops *ops; + + if (flavor >= RPC_MAXFLAVOR || !(ops = auth_flavors[flavor])) + return NULL; + clnt->cl_auth = ops->create(clnt); + return clnt->cl_auth; +} + +void +rpcauth_destroy(struct rpc_auth *auth) +{ + auth->au_ops->destroy(auth); +} + +/* + * Initialize RPC credential cache + */ +void +rpcauth_init_credcache(struct rpc_auth *auth) +{ + memset(auth->au_credcache, 0, sizeof(auth->au_credcache)); + auth->au_nextgc = jiffies + (auth->au_expire >> 1); +} + +/* + * Clear the RPC credential cache + */ +void +rpcauth_free_credcache(struct rpc_auth *auth) +{ + struct rpc_cred **q, *cred; + void (*destroy)(struct rpc_cred *); + int i; + + if (!(destroy = auth->au_ops->crdestroy)) + destroy = (void (*)(struct rpc_cred *)) rpc_free; + + for (i = 0; i < RPC_CREDCACHE_NR; i++) { + q = &auth->au_credcache[i]; + while ((cred = *q) != NULL) { + *q = cred->cr_next; + destroy(cred); + } + } +} + +/* + * Remove stale credentials. Avoid sleeping inside the loop. + */ +static void +rpcauth_gc_credcache(struct rpc_auth *auth) +{ + struct rpc_cred **q, *cred, *free = NULL; + int i, safe = 0; + + dprintk("RPC: gc'ing RPC credentials for auth %p\n", auth); + for (i = 0; i < RPC_CREDCACHE_NR; i++) { + q = &auth->au_credcache[i]; + while ((cred = *q) != NULL) { + if (++safe > 500) { + printk("RPC: rpcauth_gc_credcache looping!\n"); + break; + } + if (!cred->cr_count && cred->cr_expire < jiffies) { + *q = cred->cr_next; + cred->cr_next = free; + free = cred; + continue; + } + q = &cred->cr_next; + } + } + while ((cred = free) != NULL) { + free = cred->cr_next; + rpc_free(cred); + } + auth->au_nextgc = jiffies + auth->au_expire; +} + +/* + * Insert credential into cache + */ +inline void +rpcauth_insert_credcache(struct rpc_auth *auth, struct rpc_cred *cred) +{ + int nr; + + nr = (cred->cr_uid % RPC_CREDCACHE_NR); + cred->cr_next = auth->au_credcache[nr]; + auth->au_credcache[nr] = cred; + cred->cr_expire = jiffies + auth->au_expire; + cred->cr_count++; +} + +/* + * Look up a process' credentials in the authentication cache + */ +static struct rpc_cred * +rpcauth_lookup_credcache(struct rpc_task *task) +{ + struct rpc_auth *auth = task->tk_auth; + struct rpc_cred **q, *cred = NULL; + int nr; + + nr = RPC_DO_ROOTOVERRIDE(task)? 0 : (current->uid % RPC_CREDCACHE_NR); + + if (auth->au_nextgc < jiffies) + rpcauth_gc_credcache(auth); + + q = &auth->au_credcache[nr]; + while ((cred = *q) != NULL) { + if (auth->au_ops->crmatch(task, cred)) { + *q = cred->cr_next; + break; + } + q = &cred->cr_next; + } + + if (!cred) + cred = auth->au_ops->crcreate(task); + + rpcauth_insert_credcache(auth, cred); + + return (struct rpc_cred *) cred; +} + +/* + * Remove cred handle from cache + */ +static inline void +rpcauth_remove_credcache(struct rpc_auth *auth, struct rpc_cred *cred) +{ + struct rpc_cred **q, *cr; + int nr; + + nr = (cred->cr_uid % RPC_CREDCACHE_NR); + q = &auth->au_credcache[nr]; + while ((cr = *q) != NULL) { + if (cred == cr) { + *q = cred->cr_next; + return; + } + q = &cred->cr_next; + } +} + +struct rpc_cred * +rpcauth_lookupcred(struct rpc_task *task) +{ + dprintk("RPC: %4d looking up %s cred\n", + task->tk_pid, task->tk_auth->au_ops->au_name); + return task->tk_cred = rpcauth_lookup_credcache(task); +} + +int +rpcauth_matchcred(struct rpc_task *task, struct rpc_cred *cred) +{ + struct rpc_auth *auth = task->tk_auth; + + dprintk("RPC: %4d matching %s cred %p\n", + task->tk_pid, auth->au_ops->au_name, task->tk_cred); + return auth->au_ops->crmatch(task, cred); +} + +void +rpcauth_holdcred(struct rpc_task *task) +{ + dprintk("RPC: %4d holding %s cred %p\n", + task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_cred); + if (task->tk_cred) + task->tk_cred->cr_count++; +} + +void +rpcauth_releasecred(struct rpc_task *task) +{ + struct rpc_auth *auth = task->tk_auth; + struct rpc_cred *cred; + + dprintk("RPC: %4d releasing %s cred %p\n", + task->tk_pid, auth->au_ops->au_name, task->tk_cred); + if ((cred = task->tk_cred) != NULL) { + cred->cr_count--; + if (cred->cr_flags & RPCAUTH_CRED_DEAD) { + rpcauth_remove_credcache(auth, cred); + if (!cred->cr_count) + auth->au_ops->crdestroy(cred); + } + task->tk_cred = NULL; + } +} + +u32 * +rpcauth_marshcred(struct rpc_task *task, u32 *p) +{ + struct rpc_auth *auth = task->tk_auth; + + dprintk("RPC: %4d marshaling %s cred %p\n", + task->tk_pid, auth->au_ops->au_name, task->tk_cred); + return auth->au_ops->crmarshal(task, p, + task->tk_flags & RPC_CALL_REALUID); +} + +u32 * +rpcauth_checkverf(struct rpc_task *task, u32 *p) +{ + struct rpc_auth *auth = task->tk_auth; + + dprintk("RPC: %4d validating %s cred %p\n", + task->tk_pid, auth->au_ops->au_name, task->tk_cred); + return auth->au_ops->crvalidate(task, p); +} + +int +rpcauth_refreshcred(struct rpc_task *task) +{ + struct rpc_auth *auth = task->tk_auth; + + dprintk("RPC: %4d refreshing %s cred %p\n", + task->tk_pid, auth->au_ops->au_name, task->tk_cred); + task->tk_status = auth->au_ops->crrefresh(task); + return task->tk_status; +} + +void +rpcauth_invalcred(struct rpc_task *task) +{ + dprintk("RPC: %4d invalidating %s cred %p\n", + task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_cred); + if (task->tk_cred) + task->tk_cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; +} + +int +rpcauth_uptodatecred(struct rpc_task *task) +{ + return !(task->tk_cred) || + (task->tk_cred->cr_flags & RPCAUTH_CRED_UPTODATE); +} diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/auth_null.c linux/net/sunrpc/auth_null.c --- v2.1.31/linux/net/sunrpc/auth_null.c Wed Dec 31 16:00:00 1969 +++ linux/net/sunrpc/auth_null.c Wed Oct 30 00:39:45 1996 @@ -0,0 +1,131 @@ +/* + * linux/net/sunrpc/rpcauth_null.c + * + * AUTH_NULL authentication. Really :-) + * + * Copyright (C) 1996, Olaf Kirch + */ + +#include +#include +#include +#include +#include +#include + +#ifdef RPC_DEBUG +# define RPCDBG_FACILITY RPCDBG_AUTH +#endif + +static struct rpc_auth * +nul_create(struct rpc_clnt *clnt) +{ + struct rpc_auth *auth; + + dprintk("RPC: creating NULL authenticator for client %p\n", clnt); + if (!(auth = (struct rpc_auth *) rpc_allocate(0, sizeof(*auth)))) + return NULL; + auth->au_cslack = 4; + auth->au_rslack = 2; + auth->au_ops = &authnull_ops; + auth->au_expire = 1800 * HZ; + rpcauth_init_credcache(auth); + + return (struct rpc_auth *) auth; +} + +static void +nul_destroy(struct rpc_auth *auth) +{ + dprintk("RPC: destroying NULL authenticator %p\n", auth); + rpc_free(auth); +} + +/* + * Create NULL creds for current process + */ +static struct rpc_cred * +nul_create_cred(struct rpc_task *task) +{ + struct rpc_cred *cred; + + if (!(cred = (struct rpc_cred *) rpc_malloc(task, sizeof(*cred)))) + return NULL; + cred->cr_count = 0; + cred->cr_flags = RPCAUTH_CRED_UPTODATE; + + return cred; +} + +/* + * Destroy cred handle. + */ +static void +nul_destroy_cred(struct rpc_cred *cred) +{ + rpc_free(cred); +} + +/* + * Match cred handle against current process + */ +static int +nul_match(struct rpc_task *task, struct rpc_cred *cred) +{ + return 1; +} + +/* + * Marshal credential. + */ +static u32 * +nul_marshal(struct rpc_task *task, u32 *p, int ruid) +{ + *p++ = htonl(RPC_AUTH_NULL); + *p++ = 0; + *p++ = htonl(RPC_AUTH_NULL); + *p++ = 0; + + return p; +} + +/* + * Refresh credential. This is a no-op for AUTH_NULL + */ +static int +nul_refresh(struct rpc_task *task) +{ + return task->tk_status = -EACCES; +} + +static u32 * +nul_validate(struct rpc_task *task, u32 *p) +{ + u32 n = ntohl(*p++); + + if (n != RPC_AUTH_NULL) { + printk("RPC: bad verf flavor: %ld\n", (unsigned long) n); + return NULL; + } + if ((n = ntohl(*p++)) != 0) { + printk("RPC: bad verf size: %ld\n", (unsigned long) n); + return NULL; + } + + return p; +} + +struct rpc_authops authnull_ops = { + RPC_AUTH_NULL, +#ifdef RPC_DEBUG + "NULL", +#endif + nul_create, + nul_destroy, + nul_create_cred, + nul_destroy_cred, + nul_match, + nul_marshal, + nul_refresh, + nul_validate +}; diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/auth_unix.c linux/net/sunrpc/auth_unix.c --- v2.1.31/linux/net/sunrpc/auth_unix.c Wed Dec 31 16:00:00 1969 +++ linux/net/sunrpc/auth_unix.c Wed Oct 30 00:39:45 1996 @@ -0,0 +1,238 @@ +/* + * linux/net/sunrpc/rpcauth_unix.c + * + * UNIX-style authentication; no AUTH_SHORT support + * + * Copyright (C) 1996, Olaf Kirch + */ + +#include +#include +#include +#include +#include +#include +#include + +struct unx_cred { + struct rpc_cred uc_base; + uid_t uc_fsuid; + gid_t uc_gid, uc_fsgid; + gid_t uc_gids[16]; +}; +#define uc_uid uc_base.cr_uid +#define uc_count uc_base.cr_count +#define uc_flags uc_base.cr_flags +#define uc_expire uc_base.cr_expire + +#define UNX_CRED_EXPIRE (60 * HZ) + +#ifndef DONT_FILLIN_HOSTNAME +/* # define UNX_MAXNODENAME (sizeof(system_utsname.nodename)-1) */ +# define UNX_MAXNODENAME 32 +# define UNX_WRITESLACK (21 + (UNX_MAXNODENAME >> 2)) +#else +# define UNX_WRITESLACK 20 +#endif + +#ifdef RPC_DEBUG +# define RPCDBG_FACILITY RPCDBG_AUTH +#endif + +static struct rpc_auth * +unx_create(struct rpc_clnt *clnt) +{ + struct rpc_auth *auth; + + dprintk("RPC: creating UNIX authenticator for client %p\n", clnt); + if (!(auth = (struct rpc_auth *) rpc_allocate(0, sizeof(*auth)))) + return NULL; + auth->au_cslack = UNX_WRITESLACK; + auth->au_rslack = 2; /* assume AUTH_NULL verf */ + auth->au_expire = UNX_CRED_EXPIRE; + auth->au_ops = &authunix_ops; + + rpcauth_init_credcache(auth); + + return auth; +} + +static void +unx_destroy(struct rpc_auth *auth) +{ + dprintk("RPC: destroying UNIX authenticator %p\n", auth); + rpcauth_free_credcache(auth); + rpc_free(auth); +} + +static struct rpc_cred * +unx_create_cred(struct rpc_task *task) +{ + struct unx_cred *cred; + int i; + + dprintk("RPC: allocating UNIX cred for uid %d gid %d\n", + current->uid, current->gid); + + if (!(cred = (struct unx_cred *) rpc_malloc(task, sizeof(*cred)))) + return NULL; + + cred->uc_count = 0; + cred->uc_flags = RPCAUTH_CRED_UPTODATE; + if (RPC_DO_ROOTOVERRIDE(task)) { + cred->uc_uid = cred->uc_fsuid = 0; + cred->uc_gid = cred->uc_fsgid = 0; + cred->uc_gids[0] = NOGROUP; + } else { + cred->uc_uid = current->uid; + cred->uc_gid = current->gid; + cred->uc_fsuid = current->fsuid; + cred->uc_fsgid = current->fsgid; + for (i = 0; i < 16 && i < NGROUPS; i++) + cred->uc_gids[i] = (gid_t) current->groups[i]; + } + + return (struct rpc_cred *) cred; +} + +struct rpc_cred * +authunix_fake_cred(struct rpc_task *task, uid_t uid, gid_t gid) +{ + struct unx_cred *cred; + + dprintk("RPC: allocating fake UNIX cred for uid %d gid %d\n", + uid, gid); + + if (!(cred = (struct unx_cred *) rpc_malloc(task, sizeof(*cred)))) + return NULL; + + cred->uc_count = 1; + cred->uc_flags = RPCAUTH_CRED_DEAD|RPCAUTH_CRED_UPTODATE; + cred->uc_uid = uid; + cred->uc_gid = gid; + cred->uc_fsuid = uid; + cred->uc_fsgid = gid; + cred->uc_gids[0] = (gid_t) NOGROUP; + + return task->tk_cred = (struct rpc_cred *) cred; +} + +static void +unx_destroy_cred(struct rpc_cred *cred) +{ + rpc_free(cred); +} + +/* + * Match credentials against current process creds. + * The root_override argument takes care of cases where the caller may + * request root creds (e.g. for NFS swapping). + */ +static int +unx_match(struct rpc_task * task, struct rpc_cred *rcred) +{ + struct unx_cred *cred = (struct unx_cred *) rcred; + int i; + + if (!RPC_DO_ROOTOVERRIDE(task)) { + if (cred->uc_uid != current->uid + || cred->uc_gid != current->gid + || cred->uc_fsuid != current->fsuid + || cred->uc_fsgid != current->fsgid) + return 0; + + for (i = 0; i < 16 && i < NGROUPS; i++) + if (cred->uc_gids[i] != (gid_t) current->groups[i]) + return 0; + return 1; + } + return (cred->uc_uid == 0 && cred->uc_fsuid == 0 + && cred->uc_gid == 0 && cred->uc_fsgid == 0 + && cred->uc_gids[0] == (gid_t) NOGROUP); +} + +/* + * Marshal credentials. + * Maybe we should keep a cached credential for performance reasons. + */ +static u32 * +unx_marshal(struct rpc_task *task, u32 *p, int ruid) +{ + struct unx_cred *cred = (struct unx_cred *) task->tk_cred; + u32 *base, *hold; + int i, n; + + *p++ = htonl(RPC_AUTH_UNIX); + base = p++; + *p++ = htonl(jiffies/HZ); +#ifndef DONT_FILLIN_HOSTNAME + if ((n = strlen((char *) system_utsname.nodename)) > UNX_MAXNODENAME) + n = UNX_MAXNODENAME; + *p++ = htonl(n); + memcpy(p, system_utsname.nodename, n); + p += (n + 3) >> 2; +#else + *p++ = 0; +#endif + if (ruid) { + *p++ = htonl((u32) cred->uc_uid); + *p++ = htonl((u32) cred->uc_gid); + } else { + *p++ = htonl((u32) cred->uc_fsuid); + *p++ = htonl((u32) cred->uc_fsgid); + } + hold = p++; + for (i = 0; i < 16 && cred->uc_gids[i] != (gid_t) NOGROUP; i++) + *p++ = htonl((u32) cred->uc_gids[i]); + *hold = htonl(p - hold - 1); /* gid array length */ + *base = htonl((p - base - 1) << 2); /* cred length */ + + *p++ = htonl(RPC_AUTH_NULL); + *p++ = htonl(0); + + return p; +} + +/* + * Refresh credentials. This is a no-op for AUTH_UNIX + */ +static int +unx_refresh(struct rpc_task *task) +{ + task->tk_cred->cr_flags |= RPCAUTH_CRED_UPTODATE; + return task->tk_status = -EACCES; +} + +static u32 * +unx_validate(struct rpc_task *task, u32 *p) +{ + u32 n = ntohl(*p++); + + if (n != RPC_AUTH_NULL && n != RPC_AUTH_UNIX && n != RPC_AUTH_SHORT) { + printk("RPC: bad verf flavor: %ld\n", (unsigned long) n); + return NULL; + } + if ((n = ntohl(*p++)) > 400) { + printk("RPC: giant verf size: %ld\n", (unsigned long) n); + return NULL; + } + task->tk_auth->au_rslack = (n >> 2) + 2; + p += (n >> 2); + + return p; +} + +struct rpc_authops authunix_ops = { + RPC_AUTH_UNIX, +#ifdef RPC_DEBUG + "UNIX", +#endif + unx_create, + unx_destroy, + unx_create_cred, + unx_destroy_cred, + unx_match, + unx_marshal, + unx_refresh, + unx_validate +}; diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/clnt.c linux/net/sunrpc/clnt.c --- v2.1.31/linux/net/sunrpc/clnt.c Wed Dec 31 16:00:00 1969 +++ linux/net/sunrpc/clnt.c Thu Mar 20 08:09:20 1997 @@ -0,0 +1,761 @@ +/* + * linux/net/sunrpc/rpcclnt.c + * + * This file contains the high-level RPC interface. + * It is modeled as a finite state machine to support both synchronous + * and asynchronous requests. + * + * - RPC header generation and argument serialization. + * - Credential refresh. + * - TCP reconnect handling (when finished). + * - Retry of operation when it is suspected the operation failed because + * of uid squashing on the server, or when the credentials were stale + * and need to be refreshed, or when a packet was damaged in transit. + * This may be have to be moved to the VFS layer. + * + * NB: BSD uses a more intelligent approach to guessing when a request + * or reply has been lost by keeping the RTO estimate for each procedure. + * We currently make do with a constant timeout value. + * + * Copyright (C) 1992,1993 Rick Sladkey + * Copyright (C) 1995,1996 Olaf Kirch + */ + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + + +#define RPC_SLACK_SPACE 1024 /* total overkill */ + +#ifdef RPC_DEBUG +# define RPCDBG_FACILITY RPCDBG_CALL +#endif + +static struct wait_queue * destroy_wait = NULL; + + +static void call_bind(struct rpc_task *task); +static void call_reserve(struct rpc_task *task); +static void call_reserveresult(struct rpc_task *task); +static void call_allocate(struct rpc_task *task); +static void call_encode(struct rpc_task *task); +static void call_decode(struct rpc_task *task); +static void call_transmit(struct rpc_task *task); +static void call_receive(struct rpc_task *task); +static void call_status(struct rpc_task *task); +static void call_refresh(struct rpc_task *task); +static void call_refreshresult(struct rpc_task *task); +static void call_timeout(struct rpc_task *task); +static void call_reconnect(struct rpc_task *task); +static u32 * call_header(struct rpc_task *task); +static u32 * call_verify(struct rpc_task *task); + +#define _S(nr) (1 << ((nr) - 1)) + +/* + * Create an RPC client + * FIXME: This should also take a flags argument (as in task->tk_flags). + * It's called (among others) from pmap_create_client, which may in + * turn be called by an async task. In this case, rpciod should not be + * made to sleep too long. + */ +struct rpc_clnt * +rpc_create_client(struct rpc_xprt *xprt, char *servname, + struct rpc_program *program, u32 vers, int flavor) +{ + struct rpc_version *version; + struct rpc_clnt *clnt; + + dprintk("RPC: creating %s client for %s (xprt %p)\n", + program->name, servname, xprt); + + if (!xprt) + return NULL; + if (vers>= program->nrvers || !(version = program->version[vers])) + return NULL; + + if (!(clnt = (struct rpc_clnt *) rpc_allocate(0, sizeof(*clnt)))) { + printk("RPC: out of memory in rpc_create_client\n"); + return NULL; + } + memset(clnt, 0, sizeof(*clnt)); + + clnt->cl_xprt = xprt; + clnt->cl_procinfo = version->procs; + clnt->cl_maxproc = version->nrprocs; + clnt->cl_server = servname; + clnt->cl_protname = program->name; + clnt->cl_port = xprt->addr.sin_port; + clnt->cl_prog = program->number; + clnt->cl_vers = version->number; + clnt->cl_prot = IPPROTO_UDP; + clnt->cl_stats = program->stats; + clnt->cl_bindwait = RPC_INIT_WAITQ("bindwait"); + + if (!clnt->cl_port) + clnt->cl_autobind = 1; + + if (!rpcauth_create(flavor, clnt)) { + printk("RPC: Couldn't create auth handle (flavor %d)\n", + flavor); + rpc_free(clnt); + return NULL; + } + return clnt; +} + +/* + * Properly shut down an RPC client, terminating all outstanding + * requests. + */ +int +rpc_shutdown_client(struct rpc_clnt *clnt) +{ + dprintk("RPC: shutting down %s client for %s\n", + clnt->cl_protname, clnt->cl_server); + while (clnt->cl_users) { + dprintk("sigmask %08lx\n", current->signal); + dprintk("users %d\n", clnt->cl_users); + clnt->cl_dead = 1; + rpc_killall_tasks(clnt); + sleep_on(&destroy_wait); + } + return rpc_destroy_client(clnt); +} + +/* + * Delete an RPC client + */ +int +rpc_destroy_client(struct rpc_clnt *clnt) +{ + dprintk("RPC: destroying %s client for %s\n", + clnt->cl_protname, clnt->cl_server); + + rpcauth_destroy(clnt->cl_auth); + if (clnt->cl_xprt) { + xprt_destroy(clnt->cl_xprt); + clnt->cl_xprt = NULL; + } + rpc_free(clnt); + return 0; +} + +/* + * Release an RPC client + */ +void +rpc_release_client(struct rpc_clnt *clnt) +{ + dprintk("RPC: rpc_release_client(%p, %d)\n", + clnt, clnt->cl_users); + if (--(clnt->cl_users) == 0) { + wake_up(&destroy_wait); + if (clnt->cl_oneshot || clnt->cl_dead) + rpc_destroy_client(clnt); + } + dprintk("RPC: rpc_release_client done\n"); +} + +/* + * Default callback for async RPC calls + */ +static void +rpc_default_callback(struct rpc_task *task) +{ + rpc_release_task(task); +} + +/* + * New rpc_call implementation + */ +int +rpc_do_call(struct rpc_clnt *clnt, u32 proc, void *argp, void *resp, + int flags, rpc_action func, void *data) +{ + struct rpc_task my_task, *task = &my_task; + unsigned long oldmask, sigallow = _S(SIGKILL); + int async, status; + + /* Turn off various signals */ + if (clnt->cl_intr) { + struct sigaction *action = current->sig->action; + if (action[SIGINT-1].sa_handler == SIG_DFL) + sigallow |= _S(SIGINT); + if (action[SIGQUIT-1].sa_handler == SIG_DFL) + sigallow |= _S(SIGQUIT); + } + oldmask = current->blocked; + current->blocked |= ~sigallow; + + /* Create/initialize a new RPC task */ + if ((async = (flags & RPC_TASK_ASYNC)) != 0) { + if (!func) + func = rpc_default_callback; + if (!(task = rpc_new_task(clnt, func, flags))) { + current->blocked = oldmask; + return -ENOMEM; + } + task->tk_calldata = data; + } else { + rpc_init_task(task, clnt, NULL, flags); + } + + /* Bind the user cred, set up the call info struct and + * execute the task */ + if (rpcauth_lookupcred(task) != NULL) { + rpc_call_setup(task, proc, argp, resp, 0); + rpc_execute(task); + } else + async = 0; + + if (!async) { + status = task->tk_status; + rpc_release_task(task); + } else + status = 0; + + current->blocked = oldmask; + return status; +} + + +void +rpc_call_setup(struct rpc_task *task, u32 proc, + void *argp, void *resp, int flags) +{ + task->tk_action = call_bind; + task->tk_proc = proc; + task->tk_argp = argp; + task->tk_resp = resp; + task->tk_flags |= flags; + + /* Increment call count */ + rpcproc_count(task->tk_client, proc)++; +} + +/* + * Restart an (async) RPC call. Usually called from within the + * exit handler. + */ +void +rpc_restart_call(struct rpc_task *task) +{ + if (task->tk_flags & RPC_TASK_KILLED) { + rpc_release_task(task); + return; + } + task->tk_action = call_bind; + rpcproc_count(task->tk_client, task->tk_proc)++; +} + +/* + * 0. Get the server port number if not yet set + */ +static void +call_bind(struct rpc_task *task) +{ + struct rpc_clnt *clnt = task->tk_client; + + task->tk_action = call_reserve; + task->tk_status = 0; + if (!clnt->cl_port) + rpc_getport(task, clnt); +} + +/* + * 1. Reserve an RPC call slot + */ +static void +call_reserve(struct rpc_task *task) +{ + struct rpc_clnt *clnt = task->tk_client; + + dprintk("RPC: %4d call_reserve\n", task->tk_pid); + if (!clnt->cl_port) { + printk(KERN_NOTICE "%s: couldn't bind to server %s - %s.\n", + clnt->cl_protname, clnt->cl_server, + clnt->cl_softrtry? "giving up" : "retrying"); + if (!clnt->cl_softrtry) { + rpc_delay(task, 5*HZ); + return; + } + rpc_exit(task, -EIO); + return; + } + if (!rpcauth_uptodatecred(task)) { + task->tk_action = call_refresh; + return; + } + task->tk_action = call_reserveresult; + task->tk_timeout = clnt->cl_timeout.to_resrvval; + task->tk_status = 0; + clnt->cl_stats->rpccnt++; + xprt_reserve(task); +} + +/* + * 1b. Grok the result of xprt_reserve() + */ +static void +call_reserveresult(struct rpc_task *task) +{ + dprintk("RPC: %4d call_reserveresult (status %d)\n", + task->tk_pid, task->tk_status); + + if (task->tk_status >= 0) { + task->tk_action = call_allocate; + } else if (task->tk_status == -EAGAIN) { + task->tk_timeout = task->tk_client->cl_timeout.to_resrvval; + task->tk_status = 0; + xprt_reserve(task); + return; + } else if (task->tk_status == -ETIMEDOUT) { + task->tk_action = call_timeout; + } else { + task->tk_action = NULL; + } + if (!task->tk_rqstp) + rpc_exit(task, -EIO); +} + +/* + * 2. Allocate the buffer. For details, see sched.c:rpc_malloc. + * (Note: buffer memory is freed in rpc_task_release). + */ +static void +call_allocate(struct rpc_task *task) +{ + struct rpc_clnt *clnt = task->tk_client; + unsigned int bufsiz; + + dprintk("RPC: %4d call_allocate (status %d)\n", + task->tk_pid, task->tk_status); + task->tk_action = call_encode; + if (task->tk_buffer) + return; + + /* FIXME: compute buffer requirements more exactly using + * auth->au_wslack */ + bufsiz = rpcproc_bufsiz(clnt, task->tk_proc) + RPC_SLACK_SPACE; + + if ((task->tk_buffer = rpc_malloc(task, bufsiz)) != NULL) + return; + + if (!signalled()) { + xprt_release(task); + task->tk_action = call_reserve; + rpc_delay(task, HZ); + return; + } + + rpc_exit(task, -ERESTARTSYS); +} + +/* + * 3. Encode arguments of an RPC call + */ +static void +call_encode(struct rpc_task *task) +{ + struct rpc_clnt *clnt = task->tk_client; + struct rpc_rqst *req = task->tk_rqstp; + unsigned int bufsiz; + kxdrproc_t encode; + int status; + u32 *p; + + dprintk("RPC: %4d call_encode (status %d)\n", + task->tk_pid, task->tk_status); + + task->tk_action = call_transmit; + + /* Default buffer setup */ + bufsiz = rpcproc_bufsiz(clnt, task->tk_proc)+RPC_SLACK_SPACE; + req->rq_svec[0].iov_base = task->tk_buffer; + req->rq_svec[0].iov_len = bufsiz; + req->rq_slen = 0; + req->rq_snr = 1; + req->rq_rvec[0].iov_base = task->tk_buffer; + req->rq_rvec[0].iov_len = bufsiz; + req->rq_rlen = bufsiz; + req->rq_rnr = 1; + + if (task->tk_proc > clnt->cl_maxproc) { + printk(KERN_WARNING "%s (vers %d): bad procedure number %d\n", + clnt->cl_protname, clnt->cl_vers, task->tk_proc); + rpc_exit(task, -EIO); + return; + } + + /* Encode header and provided arguments */ + encode = rpcproc_encode(clnt, task->tk_proc); + if (!(p = call_header(task))) { + rpc_exit(task, -EIO); + } else + if ((status = encode(req, p, task->tk_argp)) < 0) { + printk(KERN_WARNING "%s: can't encode arguments: %d\n", + clnt->cl_protname, -status); + rpc_exit(task, status); + } +} + +/* + * 4. Transmit the RPC request + */ +static void +call_transmit(struct rpc_task *task) +{ + dprintk("RPC: %4d call_transmit (status %d)\n", + task->tk_pid, task->tk_status); + + task->tk_action = call_receive; + task->tk_status = 0; + xprt_transmit(task); +} + +/* + * 5. Wait for the RPC reply + */ +static void +call_receive(struct rpc_task *task) +{ + dprintk("RPC: %4d call_receive (status %d)\n", + task->tk_pid, task->tk_status); + + /* In case of error, evaluate status */ + if (task->tk_status < 0) { + task->tk_action = call_status; + return; + } + + /* If we have no decode function, this means we're performing + * a void call (a la lockd message passing). */ + if (!rpcproc_decode(task->tk_client, task->tk_proc)) { + rpc_remove_wait_queue(task); /* remove from xprt_pending */ + task->tk_action = NULL; + return; + } + + task->tk_action = call_status; + xprt_receive(task); +} + +/* + * 6. Sort out the RPC call status + */ +static void +call_status(struct rpc_task *task) +{ + struct rpc_clnt *clnt = task->tk_client; + struct rpc_rqst *req; + int status = task->tk_status; + + dprintk("RPC: %4d call_status (status %d)\n", + task->tk_pid, task->tk_status); + + if (status >= 0) { + task->tk_action = call_decode; + } else if (status == -ETIMEDOUT) { + task->tk_action = call_timeout; + } else if (status == -EAGAIN) { + if (!(req = task->tk_rqstp)) + task->tk_action = call_reserve; + else if (!task->tk_buffer) + task->tk_action = call_allocate; + else if (req->rq_damaged) + task->tk_action = call_encode; + else + task->tk_action = call_transmit; + } else if (status == -ENOTCONN) { + task->tk_action = call_reconnect; + } else if (status == -ECONNREFUSED && clnt->cl_autobind) { + task->tk_action = call_bind; + clnt->cl_port = 0; + } else { + if (clnt->cl_chatty) + printk("%s: RPC call returned error %d\n", + clnt->cl_protname, -status); + task->tk_action = NULL; + return; + } +} + +/* + * 6a. Handle RPC timeout + * We do not release the request slot, so we keep using the + * same XID for all retransmits. + */ +static void +call_timeout(struct rpc_task *task) +{ + struct rpc_clnt *clnt = task->tk_client; + struct rpc_rqst *req = task->tk_rqstp; + + if (req) { + struct rpc_timeout *to = &req->rq_timeout; + + if (xprt_adjust_timeout(to)) { + dprintk("RPC: %4d call_timeout (minor timeo)\n", + task->tk_pid); + goto minor_timeout; + } + if ((to->to_initval <<= 1) > to->to_maxval) + to->to_initval = to->to_maxval; + } + + dprintk("RPC: %4d call_timeout (major timeo)\n", task->tk_pid); + if (clnt->cl_softrtry) { + if (clnt->cl_chatty && !task->tk_exit) + printk("%s: server %s not responding, timed out\n", + clnt->cl_protname, clnt->cl_server); + rpc_exit(task, -EIO); + return; + } + if (clnt->cl_chatty && !(task->tk_flags & RPC_CALL_MAJORSEEN)) { + printk("%s: server %s not responding, still trying\n", + clnt->cl_protname, clnt->cl_server); + task->tk_flags |= RPC_CALL_MAJORSEEN; + } + if (clnt->cl_autobind) + clnt->cl_port = 0; + +minor_timeout: + if (!clnt->cl_port) { + task->tk_action = call_bind; + } else if (!req) { + task->tk_action = call_reserve; + } else if (req->rq_damaged) { + task->tk_action = call_encode; + clnt->cl_stats->rpcretrans++; + } else { + task->tk_action = call_transmit; + clnt->cl_stats->rpcretrans++; + } + task->tk_status = 0; +} + +/* + * 6b. Reconnect to the RPC server (TCP case) + */ +static void +call_reconnect(struct rpc_task *task) +{ + dprintk("RPC: %4d call_reconnect status %d\n", + task->tk_pid, task->tk_status); + if (task->tk_status == 0) { + task->tk_action = call_status; + task->tk_status = -EAGAIN; + return; + } + task->tk_client->cl_stats->netreconn++; + xprt_reconnect(task); +} + +/* + * 7. Decode the RPC reply + */ +static void +call_decode(struct rpc_task *task) +{ + struct rpc_clnt *clnt = task->tk_client; + struct rpc_rqst *req = task->tk_rqstp; + kxdrproc_t decode = rpcproc_decode(clnt, task->tk_proc); + u32 *p; + + dprintk("RPC: %4d call_decode (status %d)\n", + task->tk_pid, task->tk_status); + + if (clnt->cl_chatty && (task->tk_flags & RPC_CALL_MAJORSEEN)) { + printk("%s: server %s OK\n", + clnt->cl_protname, clnt->cl_server); + task->tk_flags &= ~RPC_CALL_MAJORSEEN; + } + + if (task->tk_status < 12) { + printk("%s: too small RPC reply size (%d bytes)\n", + clnt->cl_protname, task->tk_status); + rpc_exit(task, -EIO); + return; + } + + /* Verify the RPC header */ + if (!(p = call_verify(task))) + return; + + /* + * The following is an NFS-specific hack to cater for setuid + * processes whose uid is mapped to nobody on the server. + */ + if (task->tk_client->cl_prog == 100003 && ntohl(*p) == NFSERR_PERM) { + if (RPC_IS_SETUID(task) && (task->tk_suid_retry)--) { + dprintk("RPC: %4d retry squashed uid\n", task->tk_pid); + task->tk_flags ^= RPC_CALL_REALUID; + task->tk_action = call_encode; + return; + } + } + + task->tk_action = NULL; + task->tk_status = decode(req, p, task->tk_resp); + dprintk("RPC: %4d call_decode result %d\n", task->tk_pid, + task->tk_status); +} + +/* + * 8. Refresh the credentials if rejected by the server + */ +static void +call_refresh(struct rpc_task *task) +{ + dprintk("RPC: %4d call_refresh\n", task->tk_pid); + + xprt_release(task); /* Must do to obtain new XID */ + task->tk_action = call_refreshresult; + task->tk_status = 0; + task->tk_client->cl_stats->rpcauthrefresh++; + rpcauth_refreshcred(task); +} + +/* + * 8a. Process the results of a credential refresh + */ +static void +call_refreshresult(struct rpc_task *task) +{ + dprintk("RPC: %4d call_refreshresult (status %d)\n", + task->tk_pid, task->tk_status); + + if (task->tk_status < 0) { + task->tk_status = -EACCES; + task->tk_action = NULL; + } else + task->tk_action = call_reserve; +} + +/* + * Call header serialization + */ +static u32 * +call_header(struct rpc_task *task) +{ + struct rpc_clnt *clnt = task->tk_client; + struct rpc_xprt *xprt = clnt->cl_xprt; + u32 *p = task->tk_buffer; + + /* FIXME: check buffer size? */ + if (xprt->stream) + *p++ = 0; /* fill in later */ + *p++ = task->tk_rqstp->rq_xid; /* XID */ + *p++ = htonl(RPC_CALL); /* CALL */ + *p++ = htonl(RPC_VERSION); /* RPC version */ + *p++ = htonl(clnt->cl_prog); /* program number */ + *p++ = htonl(clnt->cl_vers); /* program version */ + *p++ = htonl(task->tk_proc); /* procedure */ + return rpcauth_marshcred(task, p); +} + +/* + * Reply header verification + */ +static u32 * +call_verify(struct rpc_task *task) +{ + u32 *p = task->tk_buffer, n; + + p += 1; /* skip XID */ + + if ((n = ntohl(*p++)) != RPC_REPLY) { + printk("call_verify: not an RPC reply: %x\n", n); + goto garbage; + } + if ((n = ntohl(*p++)) != RPC_MSG_ACCEPTED) { + int error = -EACCES; + + if ((n = ntohl(*p++)) != RPC_AUTH_ERROR) { + printk("call_verify: RPC call rejected: %x\n", n); + } else + switch ((n = ntohl(*p++))) { + case RPC_AUTH_REJECTEDCRED: + case RPC_AUTH_REJECTEDVERF: + if (!task->tk_cred_retry--) + break; + dprintk("RPC: %4d call_verify: retry stale creds\n", + task->tk_pid); + rpcauth_invalcred(task); + task->tk_action = call_refresh; + return NULL; + case RPC_AUTH_BADCRED: + case RPC_AUTH_BADVERF: + /* possibly garbled cred/verf? */ + if (!task->tk_garb_retry--) + break; + dprintk("RPC: %4d call_verify: retry garbled creds\n", + task->tk_pid); + task->tk_action = call_encode; + return NULL; + case RPC_AUTH_TOOWEAK: + printk("call_verify: server requires stronger " + "authentication.\n"); + default: + printk("call_verify: unknown auth error: %x\n", n); + error = -EIO; + } + dprintk("RPC: %4d call_verify: call rejected %d\n", + task->tk_pid, n); + rpc_exit(task, error); + return NULL; + } + if (!(p = rpcauth_checkverf(task, p))) + goto garbage; /* bad verifier, retry */ + switch ((n = ntohl(*p++))) { + case RPC_SUCCESS: + return p; + case RPC_GARBAGE_ARGS: + break; /* retry */ + default: + printk("call_verify: server accept status: %x\n", n); + /* Also retry */ + } + +garbage: + dprintk("RPC: %4d call_verify: server saw garbage\n", task->tk_pid); + task->tk_client->cl_stats->rpcgarbage++; + if (0 && task->tk_garb_retry--) { + task->tk_action = call_encode; + return NULL; + } + rpc_exit(task, -EIO); + return NULL; +} + +#ifdef MODULE +int +init_module(void) +{ +#ifdef RPC_DEBUG + rpc_register_sysctl(); +#endif + rpcstat_init(); + return 0; +} + +void +cleanup_module(void) +{ +#ifdef RPC_DEBUG + rpc_unregister_sysctl(); +#endif + rpcstat_exit(); +} +#endif diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/pmap_clnt.c linux/net/sunrpc/pmap_clnt.c --- v2.1.31/linux/net/sunrpc/pmap_clnt.c Wed Dec 31 16:00:00 1969 +++ linux/net/sunrpc/pmap_clnt.c Thu Dec 12 13:37:15 1996 @@ -0,0 +1,238 @@ +/* + * linux/net/sunrpc/pmap.c + * + * Portmapper client. + * + * FIXME: In a secure environment, we may want to use an authentication + * flavor other than AUTH_NULL. + * + * Copyright (C) 1996, Olaf Kirch + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef RPC_DEBUG +# define RPCDBG_FACILITY RPCDBG_PMAP +#endif + +#define PMAP_SET 1 +#define PMAP_UNSET 2 +#define PMAP_GETPORT 3 + +static struct rpc_clnt * pmap_create(char *, struct sockaddr_in *, int); +static void pmap_getport_done(struct rpc_task *); +extern struct rpc_program pmap_program; + +/* + * Obtain the port for a given RPC service on a given host + */ +void +rpc_getport(struct rpc_task *task, struct rpc_clnt *clnt) +{ + struct rpc_portmap *map = &clnt->cl_pmap; + struct sockaddr_in *sap = &clnt->cl_xprt->addr; + struct rpc_clnt *pmap_clnt; + struct rpc_task *child; + + dprintk("RPC: %4d rpc_getport(%s, %d, %d, %d)\n", + task->tk_pid, clnt->cl_server, + map->pm_prog, map->pm_vers, map->pm_prot); + + if (clnt->cl_binding) { + rpc_sleep_on(&clnt->cl_bindwait, task, NULL, 0); + return; + } + clnt->cl_binding = 1; + + task->tk_status = 0; + if (!(pmap_clnt = pmap_create(clnt->cl_server, sap, map->pm_prot))) { + task->tk_status = -EACCES; + goto bailout; + } + if (!(child = rpc_new_child(pmap_clnt, task))) { + rpc_destroy_client(pmap_clnt); + goto bailout; + } + + /* Setup the call info struct */ + rpc_call_setup(child, PMAP_GETPORT, map, &clnt->cl_port, 0); + + /* ... and run the child task */ + rpc_run_child(task, child, pmap_getport_done); + return; + +bailout: + clnt->cl_binding = 0; + rpc_wake_up(&clnt->cl_bindwait); + task->tk_status = -EIO; + task->tk_action = NULL; +} + +static void +pmap_getport_done(struct rpc_task *task) +{ + struct rpc_clnt *clnt = task->tk_client; + + dprintk("RPC: %4d pmap_getport_done(status %d, port %d)\n", + task->tk_pid, task->tk_status, clnt->cl_port); + if (task->tk_status < 0) { + /* Make the calling task exit with an error */ + task->tk_action = NULL; + } else if (clnt->cl_port == 0) { + /* Program not registered */ + task->tk_status = -EACCES; + task->tk_action = NULL; + } else { + /* byte-swap port number first */ + clnt->cl_port = htons(clnt->cl_port); + clnt->cl_xprt->addr.sin_port = clnt->cl_port; + } + clnt->cl_binding = 0; + rpc_wake_up(&clnt->cl_bindwait); +} + +/* + * Set or unset a port registration with the local portmapper. + * port == 0 means unregister, port != 0 means register. + */ +int +rpc_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay) +{ + struct sockaddr_in sin; + struct rpc_portmap map; + struct rpc_clnt *pmap_clnt; + unsigned int error = 0; + + dprintk("RPC: registering (%d, %d, %d, %d) with portmapper.\n", + prog, vers, prot, port); + + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + if (!(pmap_clnt = pmap_create("localhost", &sin, IPPROTO_UDP))) { + dprintk("RPC: couldn't create pmap client\n"); + return -EACCES; + } + + map.pm_prog = prog; + map.pm_vers = vers; + map.pm_prot = prot; + map.pm_port = port; + + error = rpc_call(pmap_clnt, port? PMAP_SET : PMAP_UNSET, + &map, okay, 0); + + if (error < 0) { + printk(KERN_WARNING + "RPC: failed to contact portmap (errno %d).\n", + error); + } + dprintk("RPC: registration status %d/%d\n", error, *okay); + + /* Client deleted automatically because cl_oneshot == 1 */ + return error; +} + +static struct rpc_clnt * +pmap_create(char *hostname, struct sockaddr_in *srvaddr, int proto) +{ + struct rpc_xprt *xprt; + struct rpc_clnt *clnt; + + /* printk("pmap: create xprt\n"); */ + if (!(xprt = xprt_create_proto(proto, srvaddr, NULL))) + return NULL; + xprt->addr.sin_port = htons(RPC_PMAP_PORT); + + /* printk("pmap: create clnt\n"); */ + clnt = rpc_create_client(xprt, hostname, + &pmap_program, RPC_PMAP_VERSION, + RPC_AUTH_NULL); + if (!clnt) { + xprt_destroy(xprt); + } else { + clnt->cl_softrtry = 1; + clnt->cl_chatty = 1; + clnt->cl_oneshot = 1; + } + return clnt; +} + +/* + * XDR encode/decode functions for PMAP + */ +static int +xdr_error(struct rpc_rqst *req, u32 *p, void *dummy) +{ + return -EIO; +} + +static int +xdr_encode_mapping(struct rpc_rqst *req, u32 *p, struct rpc_portmap *map) +{ + dprintk("RPC: xdr_encode_mapping(%d, %d, %d, %d)\n", + map->pm_prog, map->pm_vers, map->pm_prot, map->pm_port); + *p++ = htonl(map->pm_prog); + *p++ = htonl(map->pm_vers); + *p++ = htonl(map->pm_prot); + *p++ = htonl(map->pm_port); + + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +static int +xdr_decode_port(struct rpc_rqst *req, u32 *p, unsigned short *portp) +{ + *portp = (unsigned short) ntohl(*p++); + return 0; +} + +static int +xdr_decode_bool(struct rpc_rqst *req, u32 *p, unsigned int *boolp) +{ + *boolp = (unsigned int) ntohl(*p++); + return 0; +} + +static struct rpc_procinfo pmap_procedures[4] = { + { "pmap_null", + (kxdrproc_t) xdr_error, + (kxdrproc_t) xdr_error, 0, 0 }, + { "pmap_set", + (kxdrproc_t) xdr_encode_mapping, + (kxdrproc_t) xdr_decode_bool, 4, 1 }, + { "pmap_unset", + (kxdrproc_t) xdr_encode_mapping, + (kxdrproc_t) xdr_decode_bool, 4, 1 }, + { "pmap_get", + (kxdrproc_t) xdr_encode_mapping, + (kxdrproc_t) xdr_decode_port, 4, 1 }, +}; + +static struct rpc_version pmap_version2 = { + 2, 4, pmap_procedures +}; + +static struct rpc_version * pmap_version[] = { + NULL, + NULL, + &pmap_version2, +}; + +static struct rpc_stat pmap_stats; + +struct rpc_program pmap_program = { + "portmap", + RPC_PMAP_PROGRAM, + sizeof(pmap_version)/sizeof(pmap_version[0]), + pmap_version, + &pmap_stats, +}; diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/sched.c linux/net/sunrpc/sched.c --- v2.1.31/linux/net/sunrpc/sched.c Wed Dec 31 16:00:00 1969 +++ linux/net/sunrpc/sched.c Fri Apr 4 12:47:47 1997 @@ -0,0 +1,805 @@ +/* + * linux/net/sunrpc/sched.c + * + * Scheduling for synchronous and asynchronous RPC requests. + * + * Copyright (C) 1996 Olaf Kirch, + */ + +#define __NO_VERSION__ +#include + +#define __KERNEL_SYSCALLS__ +#include +#include +#include +#include +#include +#include +#include + +#ifdef RPC_DEBUG +#define RPCDBG_FACILITY RPCDBG_SCHED +static int rpc_task_id = 0; +#endif + +#define _S(signo) (1 << ((signo)-1)) + +/* + * We give RPC the same get_free_pages priority as NFS + */ +#define GFP_RPC GFP_NFS + +static void __rpc_default_timer(struct rpc_task *task); +static void rpciod_killall(void); + +/* + * When an asynchronous RPC task is activated within a bottom half + * handler, or while executing another RPC task, it is put on + * schedq, and rpciod is woken up. + */ +static struct rpc_wait_queue schedq = RPC_INIT_WAITQ("schedq"); + +/* + * RPC tasks that create another task (e.g. for contacting the portmapper) + * will wait on this queue for their child's completion + */ +static struct rpc_wait_queue childq = RPC_INIT_WAITQ("childq"); + +/* + * All RPC tasks are linked into this list + */ +static struct rpc_task * all_tasks = NULL; + +/* + * rpciod-related stuff + */ +static struct wait_queue * rpciod_idle = NULL; +static struct wait_queue * rpciod_killer = NULL; +static int rpciod_sema = 0; +static pid_t rpciod_pid = 0; +static int rpc_inhibit = 0; + +/* + * This is the last-ditch buffer for NFS swap requests + */ +static u32 swap_buffer[PAGE_SIZE >> 2]; +static int swap_buffer_used = 0; + +/* + * Add new request to wait queue. + * + * Swapper tasks always get inserted at the head of the queue. + * This should avoid many nasty memory deadlocks and hopefully + * improve overall performance. + * Everyone else gets appended to the queue to ensure proper FIFO behavior. + */ +void +rpc_add_wait_queue(struct rpc_wait_queue *queue, struct rpc_task *task) +{ + if (task->tk_rpcwait) { + if (task->tk_rpcwait != queue) + printk(KERN_WARNING "RPC: doubly enqueued task!\n"); + return; + } + if (RPC_IS_SWAPPER(task)) + rpc_insert_list(&queue->task, task); + else + rpc_append_list(&queue->task, task); + task->tk_rpcwait = queue; + + dprintk("RPC: %4d added to queue %p \"%s\"\n", + task->tk_pid, queue, rpc_qname(queue)); +} + +/* + * Remove request from queue + */ +void +rpc_remove_wait_queue(struct rpc_task *task) +{ + struct rpc_wait_queue *queue; + + if (!(queue = task->tk_rpcwait)) + return; + rpc_remove_list(&queue->task, task); + task->tk_rpcwait = NULL; + + dprintk("RPC: %4d removed from queue %p \"%s\"\n", + task->tk_pid, queue, rpc_qname(queue)); +} + +/* + * Set up a timer for the current task. + */ +inline void +rpc_add_timer(struct rpc_task *task, rpc_action timer) +{ + unsigned long expires = jiffies + task->tk_timeout; + + dprintk("RPC: %4d setting alarm for %lu ms\n", + task->tk_pid, task->tk_timeout * 1000 / HZ); + if (!timer) + timer = __rpc_default_timer; + if (expires < jiffies) { + printk("RPC: bad timeout value %ld - setting to 10 sec!\n", + task->tk_timeout); + expires = jiffies + 10 * HZ; + } + task->tk_timer.expires = expires; + task->tk_timer.data = (unsigned long) task; + task->tk_timer.function = (void (*)(unsigned long)) timer; + task->tk_timer.prev = NULL; + task->tk_timer.next = NULL; + add_timer(&task->tk_timer); +} + +/* + * Delete any timer for the current task. + * Must be called with interrupts off. + */ +inline void +rpc_del_timer(struct rpc_task *task) +{ + if (task->tk_timeout) { + dprintk("RPC: %4d deleting timer\n", task->tk_pid); + del_timer(&task->tk_timer); + task->tk_timeout = 0; + } +} + +/* + * Make an RPC task runnable. + */ +static inline void +rpc_make_runnable(struct rpc_task *task) +{ + if (task->tk_timeout) { + printk("RPC: task w/ running timer in rpc_make_runnable!!\n"); + return; + } + if (RPC_IS_ASYNC(task)) { + rpc_add_wait_queue(&schedq, task); + wake_up(&rpciod_idle); + } else { + wake_up(&task->tk_wait); + } + task->tk_flags |= RPC_TASK_RUNNING; +} + +/* + * Prepare for sleeping on a wait queue. + * By always appending tasks to the list we ensure FIFO behavior. + * NB: An RPC task will only receive interrupt-driven events as long + * as it's on a wait queue. + */ +static void +__rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task, + rpc_action action, rpc_action timer) +{ + unsigned long oldflags; + + dprintk("RPC: %4d sleep_on(queue \"%s\" time %ld)\n", task->tk_pid, + rpc_qname(q), jiffies); + + /* + * Protect the execution below. + */ + save_flags(oldflags); cli(); + + rpc_add_wait_queue(q, task); + task->tk_callback = action; + if (task->tk_timeout) + rpc_add_timer(task, timer); + task->tk_flags &= ~RPC_TASK_RUNNING; + + restore_flags(oldflags); + return; +} + +void +rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task, + rpc_action action, rpc_action timer) +{ + __rpc_sleep_on(q, task, action, timer); +} + +/* + * Wake up a single task -- must be invoked with bottom halves off. + * + * It would probably suffice to cli/sti the del_timer and remove_wait_queue + * operations individually. + */ +static void +__rpc_wake_up(struct rpc_task *task) +{ + dprintk("RPC: %4d __rpc_wake_up (now %ld inh %d)\n", + task->tk_pid, jiffies, rpc_inhibit); + +#ifdef RPC_DEBUG + if (task->tk_magic != 0xf00baa) { + printk("RPC: attempt to wake up non-existing task!\n"); + rpc_debug = ~0; + return; + } +#endif + rpc_del_timer(task); + if (task->tk_rpcwait != &schedq) + rpc_remove_wait_queue(task); + if (!RPC_IS_RUNNING(task)) { + rpc_make_runnable(task); + task->tk_flags |= RPC_TASK_CALLBACK; + } + dprintk("RPC: __rpc_wake_up done\n"); +} + +/* + * Default timeout handler if none specified by user + */ +static void +__rpc_default_timer(struct rpc_task *task) +{ + dprintk("RPC: %d timeout (default timer)\n", task->tk_pid); + task->tk_status = -ETIMEDOUT; + task->tk_timeout = 0; + __rpc_wake_up(task); +} + +/* + * Wake up the specified task + */ +void +rpc_wake_up_task(struct rpc_task *task) +{ + unsigned long oldflags; + + save_flags(oldflags); cli(); + __rpc_wake_up(task); + restore_flags(oldflags); +} + +/* + * Wake up the next task on the wait queue. + */ +struct rpc_task * +rpc_wake_up_next(struct rpc_wait_queue *queue) +{ + unsigned long oldflags; + struct rpc_task *task; + + dprintk("RPC: wake_up_next(%p \"%s\")\n", queue, rpc_qname(queue)); + save_flags(oldflags); cli(); + if ((task = queue->task) != 0) + __rpc_wake_up(task); + restore_flags(oldflags); + + return task; +} + +/* + * Wake up all tasks on a queue + */ +void +rpc_wake_up(struct rpc_wait_queue *queue) +{ + unsigned long oldflags; + + save_flags(oldflags); cli(); + while (queue->task) + __rpc_wake_up(queue->task); + restore_flags(oldflags); +} + +/* + * Wake up all tasks on a queue, and set their status value. + */ +void +rpc_wake_up_status(struct rpc_wait_queue *queue, int status) +{ + struct rpc_task *task; + unsigned long oldflags; + + save_flags(oldflags); cli(); + while ((task = queue->task) != NULL) { + task->tk_status = status; + __rpc_wake_up(task); + } + restore_flags(oldflags); +} + +/* + * Run a task at a later time + */ +static void __rpc_atrun(struct rpc_task *); +void +rpc_delay(struct rpc_task *task, unsigned long delay) +{ + static struct rpc_wait_queue delay_queue; + + task->tk_timeout = delay; + rpc_sleep_on(&delay_queue, task, NULL, __rpc_atrun); +} + +static void +__rpc_atrun(struct rpc_task *task) +{ + task->tk_status = 0; + __rpc_wake_up(task); +} + +/* + * This is the RPC `scheduler' (or rather, the finite state machine). + */ +static int +__rpc_execute(struct rpc_task *task) +{ + unsigned long oldflags; + int status = 0; + + dprintk("RPC: %4d rpc_execute flgs %x\n", + task->tk_pid, task->tk_flags); + + if (!RPC_IS_RUNNING(task)) { + printk("RPC: rpc_execute called for sleeping task!!\n"); + return 0; + } + + while (1) { + /* + * Execute any pending callback. + */ + if (task->tk_flags & RPC_TASK_CALLBACK) { + task->tk_flags &= ~RPC_TASK_CALLBACK; + if (task->tk_callback) { + task->tk_callback(task); + task->tk_callback = NULL; + } + } + + /* + * No handler for next step means exit. + */ + if (!task->tk_action) + break; + + /* + * Perform the next FSM step. + * tk_action may be NULL when the task has been killed + * by someone else. + */ + if (RPC_IS_RUNNING(task) && task->tk_action) + task->tk_action(task); + + /* + * Check whether task is sleeping. + * Note that if the task may go to sleep in tk_action, + * and the RPC reply arrives before we get here, it will + * have state RUNNING, but will still be on schedq. + */ + save_flags(oldflags); cli(); + if (RPC_IS_RUNNING(task)) { + if (task->tk_rpcwait == &schedq) + rpc_remove_wait_queue(task); + } else while (!RPC_IS_RUNNING(task)) { + if (RPC_IS_ASYNC(task)) { + restore_flags(oldflags); + return 0; + } + + /* sync task: sleep here */ + dprintk("RPC: %4d sync task going to sleep\n", + task->tk_pid); + current->timeout = 0; + interruptible_sleep_on(&task->tk_wait); + + /* When the task received a signal, remove from + * any queues etc, and make runnable again. */ + if (signalled()) + __rpc_wake_up(task); + + dprintk("RPC: %4d sync task resuming\n", + task->tk_pid); + } + restore_flags(oldflags); + + /* + * When a sync task receives a signal, it exits with + * -ERESTARTSYS. In order to catch any callbacks that + * clean up after sleeping on some queue, we don't + * break the loop here, but go around once more. + */ + if (!RPC_IS_ASYNC(task) && signalled()) { + dprintk("RPC: %4d got signal (map %08lx)\n", + task->tk_pid, + current->signal & ~current->blocked); + rpc_exit(task, -ERESTARTSYS); + } + } + + dprintk("RPC: %4d exit() = %d\n", task->tk_pid, task->tk_status); + if (task->tk_exit) { + status = task->tk_status; + task->tk_exit(task); + } + + return status; +} + +/* + * User-visible entry point to the scheduler. + * The recursion protection is for debugging. It should go away once + * the code has stabilized. + */ +void +rpc_execute(struct rpc_task *task) +{ + static int executing = 0; + int incr = RPC_IS_ASYNC(task)? 1 : 0; + + if (incr && (executing || rpc_inhibit)) { + printk("RPC: rpc_execute called recursively!\n"); + return; + } + executing += incr; + __rpc_execute(task); + executing -= incr; +} + +/* + * This is our own little scheduler for async RPC tasks. + */ +static void +__rpc_schedule(void) +{ + struct rpc_task *task; + int count = 0; + unsigned long oldflags; + + dprintk("RPC: rpc_schedule enter\n"); + while (1) { + save_flags(oldflags); cli(); + if (!(task = schedq.task)) + break; + rpc_del_timer(task); + rpc_remove_wait_queue(task); + task->tk_flags |= RPC_TASK_RUNNING; + restore_flags(oldflags); + + __rpc_execute(task); + + if (++count >= 200) { + count = 0; + need_resched = 1; + } + if (need_resched) + schedule(); + } + restore_flags(oldflags); + dprintk("RPC: rpc_schedule leave\n"); +} + +/* + * Allocate memory for RPC purpose. + * + * This is yet another tricky issue: For sync requests issued by + * a user process, we want to make kmalloc sleep if there isn't + * enough memory. Async requests should not sleep too excessively + * because that will block rpciod (but that's not dramatic when + * it's starved of memory anyway). Finally, swapout requests should + * never sleep at all, and should not trigger another swap_out + * request through kmalloc which would just increase memory contention. + * + * I hope the following gets it right, which gives async requests + * a slight advantage over sync requests (good for writeback, debatable + * for readahead): + * + * sync user requests: GFP_KERNEL + * async requests: GFP_RPC (== GFP_NFS) + * swap requests: GFP_ATOMIC (or new GFP_SWAPPER) + */ +void * +rpc_allocate(unsigned int flags, unsigned int size) +{ + u32 *buffer; + int gfp; + + if (flags & RPC_TASK_SWAPPER) + gfp = GFP_ATOMIC; + else if (flags & RPC_TASK_ASYNC) + gfp = GFP_RPC; + else + gfp = GFP_KERNEL; + + do { + if ((buffer = (u32 *) kmalloc(size, gfp)) != NULL) { + dprintk("RPC: allocated buffer %p\n", buffer); + return buffer; + } + if ((flags & RPC_TASK_SWAPPER) && !swap_buffer_used++) { + dprintk("RPC: used last-ditch swap buffer\n"); + return swap_buffer; + } + if (flags & RPC_TASK_ASYNC) + return NULL; + current->timeout = jiffies + (HZ >> 4); + schedule(); + } while (!signalled()); + + return NULL; +} + +void +rpc_free(void *buffer) +{ + if (buffer != swap_buffer) { + kfree(buffer); + return; + } + swap_buffer_used = 0; +} + +/* + * Creation and deletion of RPC task structures + */ +inline void +rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt, + rpc_action callback, int flags) +{ + memset(task, 0, sizeof(*task)); + task->tk_client = clnt; + task->tk_flags = RPC_TASK_RUNNING | flags; + task->tk_exit = callback; + if (current->uid != current->fsuid || current->gid != current->fsgid) + task->tk_flags |= RPC_TASK_SETUID; + + /* Initialize retry counters */ + task->tk_garb_retry = 2; + task->tk_cred_retry = 2; + task->tk_suid_retry = 1; + + /* Add to global list of all tasks */ + task->tk_next_task = all_tasks; + task->tk_prev_task = NULL; + if (all_tasks) + all_tasks->tk_prev_task = task; + all_tasks = task; + + if (clnt) + clnt->cl_users++; + +#ifdef RPC_DEBUG + task->tk_magic = 0xf00baa; + task->tk_pid = rpc_task_id++; +#endif + dprintk("RPC: %4d new task procpid %d\n", task->tk_pid, + current->pid); +} + +struct rpc_task * +rpc_new_task(struct rpc_clnt *clnt, rpc_action callback, int flags) +{ + struct rpc_task *task; + + if (!(task = (struct rpc_task *) rpc_allocate(flags, sizeof(*task)))) + return NULL; + + rpc_init_task(task, clnt, callback, flags); + + dprintk("RPC: %4d allocated task\n", task->tk_pid); + task->tk_flags |= RPC_TASK_DYNAMIC; + return task; +} + +void +rpc_release_task(struct rpc_task *task) +{ + struct rpc_task *next, *prev; + + dprintk("RPC: %4d release task\n", task->tk_pid); + + /* Remove from global task list */ + prev = task->tk_prev_task; + next = task->tk_next_task; + if (next) + next->tk_prev_task = prev; + if (prev) + prev->tk_next_task = next; + else + all_tasks = next; + + /* Release resources */ + if (task->tk_rqstp) + xprt_release(task); + if (task->tk_cred) + rpcauth_releasecred(task); + if (task->tk_buffer) { + rpc_free(task->tk_buffer); + task->tk_buffer = NULL; + } + if (task->tk_client) { + rpc_release_client(task->tk_client); + task->tk_client = NULL; + } + +#ifdef RPC_DEBUG + task->tk_magic = 0; +#endif + + if (task->tk_flags & RPC_TASK_DYNAMIC) { + dprintk("RPC: %4d freeing task\n", task->tk_pid); + task->tk_flags &= ~RPC_TASK_DYNAMIC; + rpc_free(task); + } +} + +/* + * Handling of RPC child tasks + * We can't simply call wake_up(parent) here, because the + * parent task may already have gone away + */ +static inline struct rpc_task * +rpc_find_parent(struct rpc_task *child) +{ + struct rpc_task *temp, *parent; + + parent = (struct rpc_task *) child->tk_calldata; + for (temp = childq.task; temp; temp = temp->tk_next) { + if (temp == parent) + return parent; + } + return NULL; +} + +static void +rpc_child_exit(struct rpc_task *child) +{ + struct rpc_task *parent; + + if ((parent = rpc_find_parent(child)) != NULL) { + parent->tk_status = child->tk_status; + rpc_wake_up_task(parent); + } + rpc_release_task(child); +} + +struct rpc_task * +rpc_new_child(struct rpc_clnt *clnt, struct rpc_task *parent) +{ + struct rpc_task *task; + + if (!(task = rpc_new_task(clnt, NULL, RPC_TASK_ASYNC|RPC_TASK_CHILD))) { + parent->tk_status = -ENOMEM; + return NULL; + } + task->tk_exit = rpc_child_exit; + task->tk_calldata = parent; + + return task; +} + +void +rpc_run_child(struct rpc_task *task, struct rpc_task *child, rpc_action func) +{ + rpc_make_runnable(child); + rpc_sleep_on(&childq, task, func, NULL); +} + +/* + * Kill all tasks for the given client. + * XXX: kill their descendants as well? + */ +void +rpc_killall_tasks(struct rpc_clnt *clnt) +{ + struct rpc_task **q, *rovr; + + dprintk("RPC: killing all tasks for client %p\n", clnt); + rpc_inhibit++; + for (q = &all_tasks; (rovr = *q); q = &rovr->tk_next_task) { + if (!clnt || rovr->tk_client == clnt) { + rovr->tk_flags |= RPC_TASK_KILLED; + rpc_exit(rovr, -EIO); + rpc_wake_up_task(rovr); + } + } + rpc_inhibit--; +} + +/* + * This is the rpciod kernel thread + */ +static int +rpciod(void *ptr) +{ + struct wait_queue **assassin = (struct wait_queue **) ptr; + unsigned long oldflags; + int rounds = 0; + + lock_kernel(); + rpciod_pid = current->pid; + + MOD_INC_USE_COUNT; + /* exit_files(current); */ + exit_mm(current); + current->blocked |= ~_S(SIGKILL); + current->session = 1; + current->pgrp = 1; + sprintf(current->comm, "rpciod"); + + dprintk("RPC: rpciod starting (pid %d)\n", rpciod_pid); + while (rpciod_sema) { + if (signalled()) { + if (current->signal & _S(SIGKILL)) { + rpciod_killall(); + } else { + printk("rpciod: ignoring signal (%d users)\n", + rpciod_sema); + } + current->signal &= current->blocked; + } + __rpc_schedule(); + + if (++rounds >= 64) /* safeguard */ + schedule(); + save_flags(oldflags); cli(); + if (!schedq.task) { + dprintk("RPC: rpciod back to sleep\n"); + interruptible_sleep_on(&rpciod_idle); + dprintk("RPC: switch to rpciod\n"); + } + restore_flags(oldflags); + } + + dprintk("RPC: rpciod shutdown commences\n"); + if (all_tasks) { + printk("rpciod: active tasks at shutdown?!\n"); + rpciod_killall(); + } + + rpciod_pid = 0; + wake_up(assassin); + + dprintk("RPC: rpciod exiting\n"); + MOD_DEC_USE_COUNT; + return 0; +} + +static void +rpciod_killall(void) +{ + while (all_tasks) { + unsigned long oldsig = current->signal; + + current->signal = 0; + rpc_killall_tasks(NULL); + __rpc_schedule(); + current->timeout = jiffies + HZ / 100; + need_resched = 1; + schedule(); + current->signal = oldsig; + } +} + +void +rpciod_up(void) +{ + dprintk("rpciod_up pid %d sema %d\n", rpciod_pid, rpciod_sema); + if (!(rpciod_sema++) || !rpciod_pid) + kernel_thread(rpciod, &rpciod_killer, 0); +} + +void +rpciod_down(void) +{ + dprintk("rpciod_down pid %d sema %d\n", rpciod_pid, rpciod_sema); + if (--rpciod_sema > 0) + return; + + rpciod_sema = 0; + kill_proc(rpciod_pid, SIGKILL, 1); + while (rpciod_pid) { + if (signalled()) + return; + interruptible_sleep_on(&rpciod_killer); + } +} diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/stats.c linux/net/sunrpc/stats.c --- v2.1.31/linux/net/sunrpc/stats.c Wed Dec 31 16:00:00 1969 +++ linux/net/sunrpc/stats.c Fri Apr 4 12:08:27 1997 @@ -0,0 +1,267 @@ +/* + * linux/net/sunrpc/stats.c + * + * procfs-based user access to RPC statistics + * + * Everything is complicated by the fact that procfs doesn't pass the + * proc_dir_info struct in the call to get_info. We need our own + * inode_ops for /proc/net/rpc (we want to have a write op for zeroing + * the current stats, anyway). + * + * Copyright (C) 1995, 1996 Olaf Kirch + */ + +#include +#include +#include +#include +#include + +#define RPCDBG_FACILITY RPCDBG_MISC + +/* + * Generic stats object (same for clnt and svc stats). + * Must agree with first two fields of either. + */ +struct stats { + struct stats * next; + struct proc_dir_entry * entry; +}; + +/* Code disabled until updated to new dynamic /proc code */ +#if 0 + +static struct stats * rpc_stats = NULL; +static struct stats * svc_stats = NULL; + +static struct proc_dir_entry proc_rpc = { + 0, 3, "rpc", + S_IFDIR | S_IRUGO | S_IXUGO, 1, 0, 0, + 0, NULL, + NULL, NULL, + NULL, + NULL, NULL, +}; + +/* + * Register/unregister a stats file + */ +static void +stats_register(struct stats **q, struct stats *p) +{ + dprintk("RPC: registering /proc/net/rpc/%s\n", + p->entry->name); + /* return; */ + if (p->entry->low_ino) + return; + p->next = *q; + *q = p; + proc_register_dynamic(&proc_rpc, p->entry); +} + +static void +stats_unregister(struct stats **q, struct stats *p) +{ + dprintk("RPC: unregistering /proc/net/rpc/%s\n", + p->entry->name); + /* return; */ + if (!p->entry->low_ino) + return; + while (*q) { + if (*q == p) { + *q = p->next; + proc_unregister(&proc_rpc, p->entry->low_ino); + return; + } + q = &((*q)->next); + } +} + +/* + * Client stats handling + */ +void +rpcstat_register(struct rpc_stat *statp) +{ + stats_register(&rpc_stats, (struct stats *) statp); +} + +void +rpcstat_unregister(struct rpc_stat *statp) +{ + stats_unregister(&rpc_stats, (struct stats *) statp); +} + +int +rpcstat_get_info(struct rpc_stat *statp, char *buffer, + char **start, off_t offset, int length) +{ + struct rpc_program *prog = statp->program; + struct rpc_version *vers; + int len, i, j; + + len = sprintf(buffer, + "net %d %d %d %d\n", + statp->netcnt, + statp->netudpcnt, + statp->nettcpcnt, + statp->nettcpconn); + len += sprintf(buffer + len, + "rpc %d %d %d\n", + statp->rpccnt, + statp->rpcretrans, + statp->rpcauthrefresh); + + for (i = 0; i < prog->nrvers; i++) { + if (!(vers = prog->version[i])) + continue; + len += sprintf(buffer + len, "proc%d %d", + vers->number, vers->nrprocs); + for (j = 0; j < vers->nrprocs; j++) + len += sprintf(buffer + len, " %d", + vers->procs[j].p_count); + buffer[len++] = '\n'; + } + + if (offset >= len) { + *start = buffer; + return 0; + } + *start = buffer + offset; + len -= offset; + if (len > length) + len = length; + return len; +} + +/* + * Server stats handling + */ +void +svcstat_register(struct svc_stat *statp) +{ + stats_register(&svc_stats, (struct stats *) statp); +} + +void +svcstat_unregister(struct svc_stat *statp) +{ + stats_unregister(&svc_stats, (struct stats *) statp); +} + +int +svcstat_get_info(struct svc_stat *statp, char *buffer, + char **start, off_t offset, int length) +{ + struct svc_program *prog = statp->program; + struct svc_procedure *proc; + struct svc_version *vers; + int len, i, j; + + len = sprintf(buffer, + "net %d %d %d %d\n", + statp->netcnt, + statp->netudpcnt, + statp->nettcpcnt, + statp->nettcpconn); + len += sprintf(buffer + len, + "rpc %d %d %d %d %d\n", + statp->rpccnt, + statp->rpcbadfmt+statp->rpcbadauth+statp->rpcbadclnt, + statp->rpcbadfmt, + statp->rpcbadauth, + statp->rpcbadclnt); + + for (i = 0; i < prog->pg_nvers; i++) { + if (!(vers = prog->pg_vers[i]) || !(proc = vers->vs_proc)) + continue; + len += sprintf(buffer + len, "proc%d %d", i, vers->vs_nproc); + for (j = 0; j < vers->vs_nproc; j++, proc++) + len += sprintf(buffer + len, " %d", proc->pc_count); + buffer[len++] = '\n'; + } + + if (offset >= len) { + *start = buffer; + return 0; + } + *start = buffer + offset; + if ((len -= offset) > length) + len = length; + return len; +} + +/* + * Register /proc/net/rpc + */ +void +rpcstat_init(void) +{ + dprintk("RPC: registering /proc/net/rpc\n"); + proc_rpc.ops = proc_net.ops; /* cheat */ + proc_register_dynamic(&proc_net, &proc_rpc); +} + +/* + * Unregister /proc/net/rpc + */ +void +rpcstat_exit(void) +{ + while (rpc_stats) + stats_unregister(&rpc_stats, rpc_stats); + while (svc_stats) + stats_unregister(&svc_stats, svc_stats); + dprintk("RPC: unregistering /proc/net/rpc\n"); + proc_unregister(&proc_net, proc_rpc.low_ino); +} + +#else + +/* Various dummy functions */ + +int +rpcstat_get_info(struct rpc_stat *statp, char *buffer, + char **start, off_t offset, int length) +{ + return 0; +} + +int +svcstat_get_info(struct svc_stat *statp, char *buffer, + char **start, off_t offset, int length) +{ + return 0; +} + +void +rpcstat_register(struct rpc_stat *statp) +{ +} + +void +rpcstat_unregister(struct rpc_stat *statp) +{ +} + +void +svcstat_register(struct svc_stat *statp) +{ +} + +void +svcstat_unregister(struct svc_stat *statp) +{ +} + +void +rpcstat_init(void) +{ +} + +void +rpcstat_exit(void) +{ +} + +#endif diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/sunrpc_syms.c linux/net/sunrpc/sunrpc_syms.c --- v2.1.31/linux/net/sunrpc/sunrpc_syms.c Wed Dec 31 16:00:00 1969 +++ linux/net/sunrpc/sunrpc_syms.c Fri Mar 14 05:47:22 1997 @@ -0,0 +1,105 @@ +/* + * linux/net/sunrpc/sunrpc_syms.c + * + * Symbols exported by the sunrpc module. + * + * Copyright (C) 1997 Olaf Kirch + */ + +#define __NO_VERSION__ +#include +#include + +#ifdef CONFIG_MODULES + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* RPC scheduler */ +EXPORT_SYMBOL(rpc_allocate); +EXPORT_SYMBOL(rpc_free); +EXPORT_SYMBOL(rpc_execute); +EXPORT_SYMBOL(rpc_init_task); +EXPORT_SYMBOL(rpc_release_task); +EXPORT_SYMBOL(rpc_sleep_on); +EXPORT_SYMBOL(rpc_wake_up_next); +EXPORT_SYMBOL(rpc_wake_up_task); +EXPORT_SYMBOL(rpc_new_child); +EXPORT_SYMBOL(rpc_run_child); +EXPORT_SYMBOL(rpciod_down); +EXPORT_SYMBOL(rpciod_up); + +/* RPC client functions */ +EXPORT_SYMBOL(rpc_create_client); +EXPORT_SYMBOL(rpc_destroy_client); +EXPORT_SYMBOL(rpc_shutdown_client); +EXPORT_SYMBOL(rpc_do_call); +EXPORT_SYMBOL(rpc_call_setup); +EXPORT_SYMBOL(rpc_delay); +EXPORT_SYMBOL(rpc_restart_call); + +/* Client transport */ +EXPORT_SYMBOL(xprt_create_proto); +EXPORT_SYMBOL(xprt_destroy); +EXPORT_SYMBOL(xprt_set_timeout); + +/* Client credential cache */ +EXPORT_SYMBOL(rpcauth_register); +EXPORT_SYMBOL(rpcauth_unregister); +EXPORT_SYMBOL(rpcauth_init_credcache); +EXPORT_SYMBOL(rpcauth_free_credcache); +EXPORT_SYMBOL(rpcauth_insert_credcache); +EXPORT_SYMBOL(rpcauth_lookupcred); +EXPORT_SYMBOL(rpcauth_matchcred); +EXPORT_SYMBOL(rpcauth_releasecred); + +/* RPC server stuff */ +EXPORT_SYMBOL(svc_create); +EXPORT_SYMBOL(svc_create_socket); +EXPORT_SYMBOL(svc_create_thread); +EXPORT_SYMBOL(svc_exit_thread); +EXPORT_SYMBOL(svc_destroy); +EXPORT_SYMBOL(svc_drop); +EXPORT_SYMBOL(svc_process); +EXPORT_SYMBOL(svc_recv); +EXPORT_SYMBOL(svc_wake_up); + +/* RPC statistics */ +#ifdef CONFIG_PROC_FS +EXPORT_SYMBOL(rpcstat_register); +EXPORT_SYMBOL(rpcstat_unregister); +EXPORT_SYMBOL(rpcstat_get_info); +EXPORT_SYMBOL(svcstat_register); +EXPORT_SYMBOL(svcstat_unregister); +EXPORT_SYMBOL(svcstat_get_info); +#endif + +/* Generic XDR */ +EXPORT_SYMBOL(xdr_encode_string); +EXPORT_SYMBOL(xdr_decode_string); +EXPORT_SYMBOL(xdr_decode_netobj); +EXPORT_SYMBOL(xdr_encode_netobj); +EXPORT_SYMBOL(xdr_zero); +EXPORT_SYMBOL(xdr_one); + +/* RPC errors */ +EXPORT_SYMBOL(rpc_success); +EXPORT_SYMBOL(rpc_garbage_args); +EXPORT_SYMBOL(rpc_system_err); + +/* Debugging symbols */ +EXPORT_SYMBOL(rpc_debug); +EXPORT_SYMBOL(nfs_debug); +EXPORT_SYMBOL(nfsd_debug); +EXPORT_SYMBOL(nlm_debug); + +#endif /* CONFIG_MODULES */ diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/svc.c linux/net/sunrpc/svc.c --- v2.1.31/linux/net/sunrpc/svc.c Wed Dec 31 16:00:00 1969 +++ linux/net/sunrpc/svc.c Thu Mar 20 07:03:20 1997 @@ -0,0 +1,357 @@ +/* + * linux/net/sunrpc/svc.c + * + * High-level RPC service routines + * + * Copyright (C) 1995, 1996 Olaf Kirch + */ + +#define __KERNEL_SYSCALLS__ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define RPCDBG_FACILITY RPCDBG_SVCDSP + +/* + * Create an RPC service + */ +struct svc_serv * +svc_create(struct svc_program *prog, unsigned int bufsize, unsigned int xdrsize) +{ + struct svc_serv *serv; + + xdr_init(); + + if (!(serv = (struct svc_serv *) kmalloc(sizeof(*serv), GFP_KERNEL))) + return NULL; + + memset(serv, 0, sizeof(*serv)); + serv->sv_program = prog; + serv->sv_nrthreads = 1; + serv->sv_stats = prog->pg_stats; + serv->sv_bufsz = bufsize? bufsize : 4096; + serv->sv_xdrsize = xdrsize; + + serv->sv_name = prog->pg_name; + + /* Remove any stale portmap registrations */ + svc_register(serv, 0, 0); + + return serv; +} + +/* + * Destroy an RPC service + */ +void +svc_destroy(struct svc_serv *serv) +{ + struct svc_sock *svsk; + + dprintk("RPC: svc_destroy(%s, %d)\n", + serv->sv_program->pg_name, + serv->sv_nrthreads); + + if (--(serv->sv_nrthreads) != 0) + return; + while ((svsk = serv->sv_allsocks) != NULL) + svc_delete_socket(svsk); + + /* Unregister service with the portmapper */ + svc_register(serv, 0, 0); + kfree(serv); +} + +/* + * Allocate an RPC server buffer + * Later versions may do nifty things by allocating multiple pages + * of memory directly and putting them into the bufp->iov. + */ +int +svc_init_buffer(struct svc_buf *bufp, unsigned int size) +{ + if (!(bufp->area = (u32 *) kmalloc(size, GFP_KERNEL))) + return 0; + bufp->base = bufp->area; + bufp->buf = bufp->area; + bufp->len = 0; + bufp->buflen = size >> 2; + + bufp->iov[0].iov_base = bufp->area; + bufp->iov[0].iov_len = size; + bufp->nriov = 1; + + return 1; +} + +/* + * Release an RPC server buffer + */ +void +svc_release_buffer(struct svc_buf *bufp) +{ + kfree(bufp->area); + bufp->area = 0; +} + +/* + * Create a server thread + */ +int +svc_create_thread(svc_thread_fn func, struct svc_serv *serv) +{ + struct svc_rqst *rqstp = 0; + int error; + + if (!(rqstp = kmalloc(sizeof(*rqstp), GFP_KERNEL))) + return -ENOMEM; + + memset(rqstp, 0, sizeof(*rqstp)); + if (!(rqstp->rq_argp = (u32 *) kmalloc(serv->sv_xdrsize, GFP_KERNEL)) + || !(rqstp->rq_resp = (u32 *) kmalloc(serv->sv_xdrsize, GFP_KERNEL)) + || !svc_init_buffer(&rqstp->rq_defbuf, serv->sv_bufsz)) { + error = -ENOMEM; + goto failure; + } + + serv->sv_nrthreads++; + if ((error = kernel_thread((int (*)(void *)) func, rqstp, 0)) < 0) + goto failure; + + rqstp->rq_server = serv; + return 0; + +failure: + svc_exit_thread(rqstp); + return error; +} + +/* + * Destroy an RPC server thread + */ +void +svc_exit_thread(struct svc_rqst *rqstp) +{ + struct svc_serv *serv = rqstp->rq_server; + + svc_release_buffer(&rqstp->rq_defbuf); + if (rqstp->rq_resp) + kfree(rqstp->rq_resp); + if (rqstp->rq_argp) + kfree(rqstp->rq_argp); + kfree(rqstp); + + /* Release the server */ + svc_destroy(serv); +} + +/* + * Register an RPC service with the local portmapper. To + * unregister a service, call this routine with proto and port == 0. + */ +int +svc_register(struct svc_serv *serv, int proto, unsigned short port) +{ + struct svc_program *progp; + unsigned long oldsigs = 0; + int i, error = 0, dummy; + + progp = serv->sv_program; + + dprintk("RPC: svc_register(%s, %s, %d)\n", + progp->pg_name, proto == IPPROTO_UDP? "udp" : "tcp", port); + + if (!port) { + oldsigs = current->signal; + current->signal = 0; + } + + for (i = 0; i < progp->pg_nvers; i++) { + if (progp->pg_vers[i] == NULL) + continue; + error = rpc_register(progp->pg_prog, i, proto, port, &dummy); + if (error < 0) + return error; + if (port && !dummy) { + error = -EACCES; + break; + } + } + current->signal |= oldsigs; + return error; +} + +/* + * Process the RPC request. + */ +int +svc_process(struct svc_serv *serv, struct svc_rqst *rqstp) +{ + struct svc_program *progp; + struct svc_version *versp = NULL; /* compiler food */ + struct svc_procedure *procp = NULL; + struct svc_buf * argp = &rqstp->rq_argbuf; + struct svc_buf * resp = &rqstp->rq_resbuf; + kxdrproc_t xdr; + u32 *bufp, *statp; + u32 dir, prog, vers, proc, + auth_stat, rpc_stat; + + rpc_stat = rpc_success; + bufp = argp->buf; + + if (argp->len < 5) + goto dropit; + + dir = ntohl(*bufp++); + vers = ntohl(*bufp++); + + /* First words of reply: */ + svc_putlong(resp, xdr_one); /* REPLY */ + svc_putlong(resp, xdr_zero); /* ACCEPT */ + + if (dir != 0) { /* direction != CALL */ + serv->sv_stats->rpcbadfmt++; + goto dropit; /* drop request */ + } + if (vers != 2) { /* RPC version number */ + serv->sv_stats->rpcbadfmt++; + resp->buf[-1] = xdr_one; /* REJECT */ + svc_putlong(resp, xdr_zero); /* RPC_MISMATCH */ + svc_putlong(resp, xdr_two); /* Only RPCv2 supported */ + svc_putlong(resp, xdr_two); + goto error; + } + + rqstp->rq_prog = prog = ntohl(*bufp++); /* program number */ + rqstp->rq_vers = vers = ntohl(*bufp++); /* version number */ + rqstp->rq_proc = proc = ntohl(*bufp++); /* procedure number */ + + argp->buf += 5; + argp->len -= 5; + + /* + * Decode auth data, and add verifier to reply buffer. + * We do this before anything else in order to get a decent + * auth verifier. + */ + svc_authenticate(rqstp, &rpc_stat, &auth_stat); + + if (rpc_stat != rpc_success) { + serv->sv_stats->rpcbadfmt++; + svc_putlong(resp, rpc_garbage_args); + goto error; + } + + if (auth_stat != rpc_auth_ok) { + dprintk("svc: authentication failed (%ld)\n", ntohl(auth_stat)); + serv->sv_stats->rpcbadauth++; + resp->buf[-1] = xdr_one; /* REJECT */ + svc_putlong(resp, xdr_one); /* AUTH_ERROR */ + svc_putlong(resp, auth_stat); /* status */ + goto error; + return svc_send(rqstp); + } + + progp = serv->sv_program; + if (prog != progp->pg_prog) { + dprintk("svc: unknown program %d (me %d)\n", prog, progp->pg_prog); + serv->sv_stats->rpcbadfmt++; + svc_putlong(resp, rpc_prog_unavail); + goto error; + return svc_send(rqstp); + } + + versp = progp->pg_vers[vers]; + if (!versp || vers >= progp->pg_nvers) { + dprintk("svc: unknown version (%d)\n", vers); + serv->sv_stats->rpcbadfmt++; + svc_putlong(resp, rpc_prog_mismatch); + svc_putlong(resp, htonl(progp->pg_lovers)); + svc_putlong(resp, htonl(progp->pg_hivers)); + goto error; + return svc_send(rqstp); + } + + procp = versp->vs_proc + proc; + if (proc >= versp->vs_nproc || !procp->pc_func) { + dprintk("svc: unknown procedure (%d)\n", proc); + serv->sv_stats->rpcbadfmt++; + svc_putlong(resp, rpc_proc_unavail); + goto error; + return svc_send(rqstp); + } + rqstp->rq_server = serv; + rqstp->rq_procinfo = procp; + + /* Syntactic check complete */ + serv->sv_stats->rpccnt++; + + /* Build the reply header. */ + statp = resp->buf; + svc_putlong(resp, rpc_success); /* RPC_SUCCESS */ + + /* Bump per-procedure stats counter */ + procp->pc_count++; + + /* Initialize storage for argp and resp */ + memset(rqstp->rq_argp, 0, procp->pc_argsize); + memset(rqstp->rq_resp, 0, procp->pc_ressize); + + /* Call the function that processes the request. */ + if (!versp->vs_dispatch) { + /* Decode arguments */ + xdr = procp->pc_decode; + if (xdr && !xdr(rqstp, rqstp->rq_argbuf.buf, rqstp->rq_argp)) { + dprintk("svc: failed to decode args\n"); + serv->sv_stats->rpcbadfmt++; + svc_putlong(resp, rpc_garbage_args); + goto error; + } + + *statp = procp->pc_func(rqstp, + rqstp->rq_argp, + rqstp->rq_resp); + + /* Encode reply */ + if (*statp == rpc_success && (xdr = procp->pc_encode) + && !xdr(rqstp, rqstp->rq_resbuf.buf, rqstp->rq_resp)) { + dprintk("svc: failed to encode reply\n"); + /* serv->sv_stats->rpcsystemerr++; */ + *statp = rpc_system_err; + } + } else { + dprintk("svc: calling dispatcher\n"); + if (!versp->vs_dispatch(rqstp, statp)) + goto dropit; + } + + /* Check RPC status result */ + if (*statp != rpc_success) + resp->len = statp + 1 - resp->base; + + /* Release reply info */ + if (procp->pc_release) + procp->pc_release(rqstp, NULL, rqstp->rq_resp); + + if (procp->pc_encode != NULL) + return svc_send(rqstp); + +dropit: + dprintk("svc: svc_process dropit\n"); + svc_drop(rqstp); + return 0; + +error: + return svc_send(rqstp); +} diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/svcauth.c linux/net/sunrpc/svcauth.c --- v2.1.31/linux/net/sunrpc/svcauth.c Wed Dec 31 16:00:00 1969 +++ linux/net/sunrpc/svcauth.c Fri Dec 20 08:34:32 1996 @@ -0,0 +1,163 @@ +/* + * linux/net/sunrpc/svcauth.c + * + * The generic interface for RPC authentication on the server side. + * + * Copyright (C) 1995, 1996 Olaf Kirch + */ + +#include +#include +#include +#include +#include +#include + +#define RPCDBG_FACILITY RPCDBG_AUTH + +/* + * Type of authenticator function + */ +typedef void (*auth_fn_t)(struct svc_rqst *rqstp, u32 *statp, u32 *authp); + +/* + * Builtin auth flavors + */ +static void svcauth_null(struct svc_rqst *rqstp, u32 *statp, u32 *authp); +static void svcauth_unix(struct svc_rqst *rqstp, u32 *statp, u32 *authp); + +/* + * Max number of authentication flavors we support + */ +#define RPC_SVCAUTH_MAX 8 + +/* + * Table of authenticators + */ +static auth_fn_t authtab[RPC_SVCAUTH_MAX] = { + svcauth_null, + svcauth_unix, + NULL, +}; + +void +svc_authenticate(struct svc_rqst *rqstp, u32 *statp, u32 *authp) +{ + u32 flavor; + auth_fn_t func; + + *statp = rpc_success; + *authp = rpc_auth_ok; + + svc_getlong(&rqstp->rq_argbuf, flavor); + flavor = ntohl(flavor); + + dprintk("svc: svc_authenticate (%d)\n", flavor); + if (flavor >= RPC_SVCAUTH_MAX || !(func = authtab[flavor])) { + *authp = rpc_autherr_badcred; + return; + } + + rqstp->rq_cred.cr_flavor = flavor; + func(rqstp, statp, authp); +} + +int +svc_auth_register(u32 flavor, auth_fn_t func) +{ + if (flavor >= RPC_SVCAUTH_MAX || authtab[flavor]) + return -EINVAL; + authtab[flavor] = func; + return 0; +} + +void +svc_auth_unregister(u32 flavor) +{ + if (flavor < RPC_SVCAUTH_MAX) + authtab[flavor] = NULL; +} + +static void +svcauth_null(struct svc_rqst *rqstp, u32 *statp, u32 *authp) +{ + struct svc_buf *argp = &rqstp->rq_argbuf; + struct svc_buf *resp = &rqstp->rq_resbuf; + + if ((argp->len -= 3) < 0) { + *statp = rpc_garbage_args; + return; + } + if (*(argp->buf)++ != 0) { /* we already skipped the flavor */ + dprintk("svc: bad null cred\n"); + *authp = rpc_autherr_badcred; + return; + } + if (*(argp->buf)++ != RPC_AUTH_NULL || *(argp->buf)++ != 0) { + dprintk("svc: bad null verf\n"); + *authp = rpc_autherr_badverf; + return; + } + + /* Signal that mapping to nobody uid/gid is required */ + rqstp->rq_cred.cr_uid = (uid_t) -1; + rqstp->rq_cred.cr_gid = (gid_t) -1; + rqstp->rq_cred.cr_groups[0] = NOGROUP; + + /* Put NULL verifier */ + rqstp->rq_verfed = 1; + svc_putlong(resp, RPC_AUTH_NULL); + svc_putlong(resp, 0); +} + +static void +svcauth_unix(struct svc_rqst *rqstp, u32 *statp, u32 *authp) +{ + struct svc_buf *argp = &rqstp->rq_argbuf; + struct svc_buf *resp = &rqstp->rq_resbuf; + struct svc_cred *cred = &rqstp->rq_cred; + u32 *bufp = argp->buf; + int len = argp->len, slen, i; + + if ((len -= 3) < 0) { + *statp = rpc_garbage_args; + return; + } + + bufp++; /* length */ + bufp++; /* time stamp */ + slen = (ntohl(*bufp++) + 3) >> 2; /* machname length */ + if (slen > 64 || (len -= slen) < 0) + goto badcred; + bufp += slen; /* skip machname */ + + cred->cr_uid = ntohl(*bufp++); /* uid */ + cred->cr_gid = ntohl(*bufp++); /* gid */ + + slen = ntohl(*bufp++); /* gids length */ + if (slen > 16 || (len -= slen + 2) < 0) + goto badcred; + for (i = 0; i < NGROUPS && i < slen; i++) + cred->cr_groups[i] = ntohl(*bufp++); + if (i < NGROUPS) + cred->cr_groups[i] = NOGROUP; + bufp += (slen - i); + + if (*bufp++ != RPC_AUTH_NULL || *bufp++ != 0) { + *authp = rpc_autherr_badverf; + return; + } + + argp->buf = bufp; + argp->len = len; + + /* Put NULL verifier */ + rqstp->rq_verfed = 1; + svc_putlong(resp, RPC_AUTH_NULL); + svc_putlong(resp, 0); + + return; + +badcred: + *authp = rpc_autherr_badcred; +} diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/svcauth_des.c linux/net/sunrpc/svcauth_des.c --- v2.1.31/linux/net/sunrpc/svcauth_des.c Wed Dec 31 16:00:00 1969 +++ linux/net/sunrpc/svcauth_des.c Fri Jan 3 04:10:28 1997 @@ -0,0 +1,215 @@ +/* + * linux/net/sunrpc/svcauth_des.c + * + * Server-side AUTH_DES handling. + * + * Copyright (C) 1996, 1997 Olaf Kirch + */ + +#include +#include +#include +#include +#include +#include + +#define RPCDBG_FACILITY RPCDBG_AUTH + +/* + * DES cedential cache. + * The cache is indexed by fullname/key to allow for multiple sessions + * by the same user from different hosts. + * It would be tempting to use the client's IP address rather than the + * conversation key as an index, but that could become problematic for + * multi-homed hosts that distribute traffic across their interfaces. + */ +struct des_cred { + struct des_cred * dc_next; + char * dc_fullname; + u32 dc_nickname; + des_cblock dc_key; /* conversation key */ + des_cblock dc_xkey; /* encrypted conv. key */ + des_key_schedule dc_keysched; +}; + +#define ADN_FULLNAME 0 +#define ADN_NICKNAME 1 + +/* + * The default slack allowed when checking for replayed credentials + * (in milliseconds). + */ +#define DES_REPLAY_SLACK 2000 + +/* + * Make sure we don't place more than one call to the key server at + * a time. + */ +static int in_keycall = 0; + +#define FAIL(err) \ + { if (data) put_cred(data); \ + *authp = rpc_autherr_##err; \ + return; \ + } + +void +svcauth_des(struct svc_rqst *rqstp, u32 *statp, u32 *authp) +{ + struct svc_buf *argp = &rqstp->rq_argbuf; + struct svc_buf *resp = &rqstp->rq_resbuf; + struct svc_cred *cred = &rqstp->rq_cred; + struct des_cred *data = NULL; + u32 cryptkey[2]; + u32 cryptbuf[4]; + u32 *p = argp->buf; + int len = argp->len, slen, i; + + *authp = rpc_auth_ok; + + if ((argp->len -= 3) < 0) { + *statp = rpc_garbage_args; + return; + } + + p++; /* skip length field */ + namekind = ntohl(*p++); /* fullname/nickname */ + + /* Get the credentials */ + if (namekind == ADN_NICKNAME) { + /* If we can't find the cached session key, initiate a + * new session. */ + if (!(data = get_cred_bynick(*p++))) + FAIL(rejectedcred); + } else if (namekind == ADN_FULLNAME) { + p = xdr_decode_string(p, &fullname, &len, RPC_MAXNETNAMELEN); + if (p == NULL) + FAIL(badcred); + cryptkey[0] = *p++; /* get the encrypted key */ + cryptkey[1] = *p++; + cryptbuf[2] = *p++; /* get the encrypted window */ + } else { + FAIL(badcred); + } + + /* If we're just updating the key, silently discard the request. */ + if (data && data->dc_locked) { + *authp = rpc_autherr_dropit; + _put_cred(data); /* release but don't unlock */ + return; + } + + /* Get the verifier flavor and length */ + if (ntohl(*p++) != RPC_AUTH_DES && ntohl(*p++) != 12) + FAIL(badverf); + + cryptbuf[0] = *p++; /* encrypted time stamp */ + cryptbuf[1] = *p++; + cryptbuf[3] = *p++; /* 0 or window - 1 */ + + if (namekind == ADN_NICKNAME) { + status = des_ecb_encrypt((des_block *) cryptbuf, + (des_block *) cryptbuf, + data->dc_keysched, DES_DECRYPT); + } else { + /* We first have to decrypt the new session key and + * fill in the UNIX creds. */ + if (!(data = get_cred_byname(rqstp, authp, fullname, cryptkey))) + return; + status = des_cbc_encrypt((des_cblock *) cryptbuf, + (des_cblock *) cryptbuf, 16, + data->dc_keysched, + (des_cblock *) &ivec, + DES_DECRYPT); + } + if (status) { + printk("svcauth_des: DES decryption failed (status %d)\n", + status); + FAIL(badverf); + } + + /* Now check the whole lot */ + if (namekind == ADN_FULLNAME) { + unsigned long winverf; + + data->dc_window = ntohl(cryptbuf[2]); + winverf = ntohl(cryptbuf[2]); + if (window != winverf - 1) { + printk("svcauth_des: bad window verifier!\n"); + FAIL(badverf); + } + } + + /* XDR the decrypted timestamp */ + cryptbuf[0] = ntohl(cryptbuf[0]); + cryptbuf[1] = ntohl(cryptbuf[1]); + if (cryptbuf[1] > 1000000) { + dprintk("svcauth_des: bad usec value %u\n", cryptbuf[1]); + if (namekind == ADN_NICKNAME) + FAIL(rejectedverf); + FAIL(badverf); + } + + /* + * Check for replayed credentials. We must allow for reordering + * of requests by the network, and the OS scheduler, hence we + * cannot expect timestamps to be increasing monotonically. + * This opens a small security hole, therefore the replay_slack + * value shouldn't be too large. + */ + if ((delta = cryptbuf[0] - data->dc_timestamp[0]) <= 0) { + switch (delta) { + case -1: + delta = -1000000; + case 0: + delta += cryptbuf[1] - data->dc_timestamp[1]; + break; + default: + delta = -1000000; + } + if (delta < DES_REPLAY_SLACK) + FAIL(rejectedverf); +#ifdef STRICT_REPLAY_CHECKS + /* TODO: compare time stamp to last five timestamps cached + * and reject (drop?) request if a match is found. */ +#endif + } + + now = xtime; + now.tv_secs -= data->dc_window; + if (now.tv_secs < cryptbuf[0] || + (now.tv_secs == cryptbuf[0] && now.tv_usec < cryptbuf[1])) + FAIL(rejectedverf); + + /* Okay, we're done. Update the lot */ + if (namekind == ADN_FULLNAME) + data->dc_valid = 1; + data->dc_timestamp[0] = cryptbuf[0]; + data->dc_timestamp[1] = cryptbuf[1]; + + put_cred(data); + return; +garbage: + *statp = rpc_garbage_args; + return; +} + +/* + * Call the keyserver to obtain the decrypted conversation key and + * UNIX creds. We use a Linux-specific keycall extension that does + * both things in one go. + */ +static struct des_cred * +get_cred_byname(struct svc_rqst *rqstp, u32 *authp, char *fullname, u32 *cryptkey) +{ + static int in_keycall = 0; + struct des_cred *cred; + + if (in_keycall) { + *authp = rpc_autherr_dropit; + return NULL; + } + in_keycall = 1; + in_keycall = 0; + return cred; +} diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/svcsock.c linux/net/sunrpc/svcsock.c --- v2.1.31/linux/net/sunrpc/svcsock.c Wed Dec 31 16:00:00 1969 +++ linux/net/sunrpc/svcsock.c Thu Mar 20 07:03:40 1997 @@ -0,0 +1,967 @@ +/* + * linux/net/sunrpc/svcsock.c + * + * These are the RPC server socket internals. + * + * The server scheduling algorithm does not always distribute the load + * evenly when servicing a single client. May need to modify the + * svc_sock_enqueue procedure... + * + * TCP support is largely untested and may be a little slow. The problem + * is that we currently do two separate recvfrom's, one for the 4-byte + * record length, and the second for the actual record. This could possibly + * be improved by always reading a minimum size of around 100 bytes and + * tucking any superfluous bytes away in a temporary store. Still, that + * leaves write requests out in the rain. An alternative may be to peek at + * the first skb in the queue, and if it matches the next TCP sequence + * number, to extract the record marker. Yuck. + * + * Copyright (C) 1995, 1996 Olaf Kirch + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= 0x020100 +#include +#endif + +#include +#include +#include +#include + + +#define RPCDBG_FACILITY RPCDBG_SVCSOCK + + +static struct svc_sock *svc_setup_socket(struct svc_serv *, struct socket *, + int *errp, int pmap_reg); +static void svc_udp_data_ready(struct sock *, int); +static int svc_udp_recvfrom(struct svc_rqst *); +static int svc_udp_sendto(struct svc_rqst *); + + +/* + * Queue up an idle server thread. + */ +static inline void +svc_serv_enqueue(struct svc_serv *serv, struct svc_rqst *rqstp) +{ + rpc_append_list(&serv->sv_threads, rqstp); +} + +/* + * Dequeue an nfsd thread. + */ +static inline void +svc_serv_dequeue(struct svc_serv *serv, struct svc_rqst *rqstp) +{ + rpc_remove_list(&serv->sv_threads, rqstp); +} + +/* + * Release an skbuff after use + */ +static inline void +svc_release_skb(struct svc_rqst *rqstp) +{ + if (!rqstp->rq_skbuff) + return; + + dprintk("svc: releasing skb %p\n", rqstp->rq_skbuff); + skb_free_datagram(rqstp->rq_sock->sk_sk, rqstp->rq_skbuff); + rqstp->rq_skbuff = NULL; +} + +/* + * Queue up a socket with data pending. If there are idle nfsd + * processes, wake'em up. + * When calling this function, you should make sure it can't be interrupted + * by the network bottom half. + */ +static inline void +svc_sock_enqueue(struct svc_sock *svsk) +{ + struct svc_rqst *rqstp; + struct svc_serv *serv; + + if (svsk->sk_busy) { + /* Don't enqueue socket while daemon is receiving */ + dprintk("svc: socket %p not enqueued: busy\n", svsk->sk_sk); + return; + } + + /* Mark socket as busy. It will remain in this state until the + * server has processed all pending data and put the socket back + * on the idle list + */ + svsk->sk_busy = 1; + + serv = svsk->sk_server; + if ((rqstp = serv->sv_threads) != NULL) { + dprintk("svc: socket %p served by daemon %p\n", + svsk->sk_sk, rqstp); + svc_serv_dequeue(serv, rqstp); + rqstp->rq_sock = svsk; + svsk->sk_inuse++; + wake_up(&rqstp->rq_wait); + } else { + dprintk("svc: socket %p put into queue\n", svsk->sk_sk); + rpc_append_list(&serv->sv_sockets, svsk); + svsk->sk_qued = 1; + } +} + +/* + * Dequeue the first socket. + */ +static inline struct svc_sock * +svc_sock_dequeue(struct svc_serv *serv) +{ + struct svc_sock *svsk; + + disable_bh(NET_BH); + if ((svsk = serv->sv_sockets) != NULL) + rpc_remove_list(&serv->sv_sockets, svsk); + enable_bh(NET_BH); + + if (svsk) { + dprintk("svc: socket %p dequeued\n", svsk->sk_sk); + svsk->sk_qued = 0; + } + + return svsk; +} + +/* + * Having read count bytes from a socket, check whether it + * needs to be re-enqueued. + */ +static inline void +svc_sock_received(struct svc_sock *svsk, int count) +{ + disable_bh(NET_BH); + if ((svsk->sk_data -= count) < 0) { + printk(KERN_NOTICE "svc: sk_data negative!\n"); + svsk->sk_data = 0; + } + svsk->sk_rqstp = NULL; /* XXX */ + svsk->sk_busy = 0; + if (svsk->sk_conn || svsk->sk_data || svsk->sk_close) { + dprintk("svc: socket %p re-enqueued after receive\n", + svsk->sk_sk); + svc_sock_enqueue(svsk); + } + enable_bh(NET_BH); +} + +/* + * Dequeue a new connection. + */ +static inline void +svc_sock_accepted(struct svc_sock *svsk) +{ + disable_bh(NET_BH); + svsk->sk_busy = 0; + svsk->sk_conn--; + if (svsk->sk_conn || svsk->sk_data || svsk->sk_close) { + dprintk("svc: socket %p re-enqueued after accept\n", + svsk->sk_sk); + svc_sock_enqueue(svsk); + } + enable_bh(NET_BH); +} + +/* + * Release a socket after use. + */ +static inline void +svc_sock_release(struct svc_rqst *rqstp) +{ + struct svc_sock *svsk = rqstp->rq_sock; + + if (!svsk) + return; + svc_release_skb(rqstp); + rqstp->rq_sock = NULL; + if (!--(svsk->sk_inuse) && svsk->sk_dead) { + dprintk("svc: releasing dead socket\n"); + sock_release(svsk->sk_sock); + kfree(svsk); + } +} + +/* + * External function to wake up a server waiting for data + */ +void +svc_wake_up(struct svc_serv *serv) +{ + struct svc_rqst *rqstp; + + if ((rqstp = serv->sv_threads) != NULL) { + dprintk("svc: daemon %p woken up.\n", rqstp); + /* + svc_serv_dequeue(serv, rqstp); + rqstp->rq_sock = NULL; + */ + wake_up(&rqstp->rq_wait); + } +} + +/* + * Generic sendto routine + */ +static int +svc_sendto(struct svc_rqst *rqstp, struct iovec *iov, int nr) +{ + unsigned long oldfs; + struct socket *sock = rqstp->rq_sock->sk_sock; + struct msghdr msg; + int i, buflen, len; + + for (i = buflen = 0; i < nr; i++) + buflen += iov[i].iov_len; + + msg.msg_name = &rqstp->rq_addr; + msg.msg_namelen = sizeof(rqstp->rq_addr); + msg.msg_iov = iov; + msg.msg_iovlen = nr; + msg.msg_control = 0; + +#if LINUX_VERSION_CODE >= 0x020100 + msg.msg_flags = MSG_DONTWAIT; + + oldfs = get_fs(); set_fs(KERNEL_DS); + len = sock_sendmsg(sock, &msg, buflen); + set_fs(oldfs); +#else + msg.msg_flags = 0; + + oldfs = get_fs(); set_fs(KERNEL_DS); + len = sock->ops->sendmsg(sock, &msg, buflen, 1, 0); + set_fs(oldfs); +#endif + + dprintk("svc: socket %p sendto([%p %d... ], %d, %d) = %d\n", + rqstp->rq_sock, + iov[0].iov_base, iov[0].iov_len, nr, + buflen, len); + + return len; +} + +/* + * Check input queue length + */ +static int +svc_recv_available(struct svc_sock *svsk) +{ + unsigned long oldfs; + struct socket *sock = svsk->sk_sock; + int avail, err; + + oldfs = get_fs(); set_fs(KERNEL_DS); + err = sock->ops->ioctl(sock, TIOCINQ, (unsigned long) &avail); + set_fs(oldfs); + + return (err >= 0)? avail : err; +} + +/* + * Generic recvfrom routine. + */ +static int +svc_recvfrom(struct svc_rqst *rqstp, struct iovec *iov, int nr, int buflen) +{ + unsigned long oldfs; + struct msghdr msg; + struct socket *sock; + int len; + + rqstp->rq_addrlen = sizeof(rqstp->rq_addr); + sock = rqstp->rq_sock->sk_sock; + + msg.msg_name = &rqstp->rq_addr; + msg.msg_namelen = sizeof(rqstp->rq_addr); + msg.msg_iov = iov; + msg.msg_iovlen = nr; + msg.msg_control = 0; + +#if LINUX_VERSION_CODE >= 0x020100 + msg.msg_flags = MSG_DONTWAIT; + + oldfs = get_fs(); set_fs(KERNEL_DS); + len = sock_recvmsg(sock, &msg, buflen, MSG_DONTWAIT); + set_fs(oldfs); +#else + msg.msg_flags = 0; + + oldfs = get_fs(); set_fs(KERNEL_DS); + len = sock->ops->recvmsg(sock, &msg, buflen, 0, 1, &rqstp->rq_addrlen); + set_fs(oldfs); +#endif + + dprintk("svc: socket %p recvfrom(%p, %d) = %d\n", rqstp->rq_sock, + iov[0].iov_base, iov[0].iov_len, len); + + return len; +} + +/* + * INET callback when data has been received on the socket. + */ +static void +svc_udp_data_ready(struct sock *sk, int count) +{ + struct svc_sock *svsk; + + dprintk("svc: socket %p data ready (inet %p)\n", sk->user_data, sk); + + svsk = (struct svc_sock *)(sk->user_data); + if (!svsk) + return; + svsk->sk_data = 1; + svc_sock_enqueue(svsk); +} + +/* + * Receive a datagram from a UDP socket. + */ +static int +svc_udp_recvfrom(struct svc_rqst *rqstp) +{ + struct svc_sock *svsk = rqstp->rq_sock; + struct svc_serv *serv = svsk->sk_server; + struct sk_buff *skb; + u32 *data; + int err, len; + + svsk->sk_data = 0; + while ((skb = skb_recv_datagram(svsk->sk_sk, 0, 1, &err)) == NULL) { + svc_sock_received(svsk, 0); + if (err == -EAGAIN) + return err; + /* possibly an icmp error */ + dprintk("svc: recvfrom returned error %d\n", -err); + } + + /* There may be more data */ + svsk->sk_data = 1; + + len = skb->len - sizeof(struct udphdr); + data = (u32 *) (skb->h.raw + sizeof(struct udphdr)); + + rqstp->rq_skbuff = skb; + rqstp->rq_argbuf.base = data; + rqstp->rq_argbuf.buf = data; + rqstp->rq_argbuf.len = (len >> 2); + /* rqstp->rq_resbuf = rqstp->rq_defbuf; */ + rqstp->rq_prot = IPPROTO_UDP; + + /* Get sender address */ + rqstp->rq_addr.sin_family = AF_INET; + rqstp->rq_addr.sin_port = skb->h.uh->source; +#if LINUX_VERSION_CODE >= 0x020100 + rqstp->rq_addr.sin_addr.s_addr = skb->nh.iph->saddr; +#else + rqstp->rq_addr.sin_addr.s_addr = skb->saddr; +#endif + + if (serv->sv_stats) + serv->sv_stats->netudpcnt++; + + /* One down, maybe more to go... */ + svsk->sk_sk->stamp = skb->stamp; + svc_sock_received(svsk, 0); + + return len; +} + +static int +svc_udp_sendto(struct svc_rqst *rqstp) +{ + struct svc_buf *bufp = &rqstp->rq_resbuf; + int error; + + /* Set up the first element of the reply iovec. + * Any other iovecs that may be in use have been taken + * care of by the server implementation itself. + */ + /* bufp->base = bufp->area; */ + bufp->iov[0].iov_base = bufp->base; + bufp->iov[0].iov_len = bufp->len << 2; + + error = svc_sendto(rqstp, bufp->iov, bufp->nriov); + if (error == -ECONNREFUSED) + /* ICMP error on earlier request. */ + error = svc_sendto(rqstp, bufp->iov, bufp->nriov); + else if (error == -EAGAIN) + /* Ignore and wait for re-xmit */ + error = 0; + + return error; +} + +static int +svc_udp_init(struct svc_sock *svsk) +{ + svsk->sk_sk->data_ready = svc_udp_data_ready; + svsk->sk_recvfrom = svc_udp_recvfrom; + svsk->sk_sendto = svc_udp_sendto; + + return 0; +} + +/* + * A state change on a listening socket means there's a connection + * pending. + */ +static void +svc_tcp_state_change1(struct sock *sk) +{ + struct svc_sock *svsk; + + dprintk("svc: socket %p TCP (listen) state change %d\n", + sk, sk->state); + + if (sk->state != TCP_ESTABLISHED) { + /* Aborted connection, SYN_RECV or whatever... */ + return; + } + if (!(svsk = (struct svc_sock *) sk->user_data)) { + printk("svc: socket %p: no user data\n", sk); + return; + } + svsk->sk_conn++; + svc_sock_enqueue(svsk); +} + +/* + * A state change on a connected socket means it's dying or dead. + */ +static void +svc_tcp_state_change2(struct sock *sk) +{ + struct svc_sock *svsk; + + dprintk("svc: socket %p TCP (connected) state change %d (svsk %p)\n", + sk, sk->state, sk->user_data); + + if (!(svsk = (struct svc_sock *) sk->user_data)) { + printk("svc: socket %p: no user data\n", sk); + return; + } + svsk->sk_close = 1; + svc_sock_enqueue(svsk); +} + +static void +svc_tcp_data_ready(struct sock *sk, int count) +{ + struct svc_sock * svsk; + + /* Disconnect signalled through data_ready?!? */ + if (sk->state != TCP_ESTABLISHED) { + svc_tcp_state_change2(sk); + return; + } + + dprintk("svc: socket %p TCP data ready (svsk %p)\n", + sk, sk->user_data); + if (!(svsk = (struct svc_sock *)(sk->user_data))) + return; + svsk->sk_data++; + svc_sock_enqueue(svsk); +} + +/* + * Accept a TCP connection + */ +static void +svc_tcp_accept(struct svc_sock *svsk) +{ + struct sockaddr_in sin; + struct svc_serv *serv = svsk->sk_server; + struct socket *sock = svsk->sk_sock; + struct socket *newsock; + struct proto_ops *ops; + struct svc_sock *newsvsk; + int err, slen; + + dprintk("svc: tcp_accept %p sock %p\n", svsk, sock); + if (!sock) + return; + + if (!(newsock = sock_alloc())) { + printk(KERN_WARNING "%s: no more sockets!\n", serv->sv_name); + return; + } + dprintk("svc: tcp_accept %p allocated\n", newsock); + + newsock->type = sock->type; + if ((err = sock->ops->dup(newsock, sock)) < 0) { + printk(KERN_WARNING "%s: socket dup failed (err %d)!\n", + serv->sv_name, -err); + goto failed; + } + + ops = newsock->ops; + if ((err = ops->accept(sock, newsock, O_NONBLOCK)) < 0) { + printk(KERN_WARNING "%s: accept failed (err %d)!\n", + serv->sv_name, -err); + goto failed; /* aborted connection or whatever */ + } + + slen = sizeof(sin); + err = ops->getname(newsock, (struct sockaddr *) &sin, &slen, 1); + if (err < 0) { + printk(KERN_WARNING "%s: peername failed (err %d)!\n", + serv->sv_name, -err); + goto failed; /* aborted connection or whatever */ + } + + /* Ideally, we would want to reject connections from unauthorized + * hosts here, but we have no generic client tables. For now, + * we just punt connects from unprivileged ports. */ + if (ntohs(sin.sin_port) >= 1024) { + printk(KERN_WARNING + "%s: connect from unprivileged port: %08lx:%d", + serv->sv_name, + ntohl(sin.sin_addr.s_addr), ntohs(sin.sin_port)); + goto failed; + } + + dprintk("%s: connect from %08lx:%04x\n", serv->sv_name, + ntohl(sin.sin_addr.s_addr), ntohs(sin.sin_port)); + + if (!(newsvsk = svc_setup_socket(serv, newsock, &err, 0))) + goto failed; + + /* Precharge. Data may have arrived on the socket before we + * installed the data_ready callback. + */ + newsvsk->sk_data = 1; + newsvsk->sk_temp = 1; + svc_sock_enqueue(newsvsk); + + if (serv->sv_stats) + serv->sv_stats->nettcpconn++; + + return; + +failed: + sock_release(newsock); + return; +} + +/* + * Receive data from a TCP socket. + */ +static int +svc_tcp_recvfrom(struct svc_rqst *rqstp) +{ + struct svc_sock *svsk = rqstp->rq_sock; + struct svc_serv *serv = svsk->sk_server; + struct svc_buf *bufp = &rqstp->rq_argbuf; + int len, ready; + + dprintk("svc: tcp_recv %p data %d conn %d close %d\n", + svsk, svsk->sk_data, svsk->sk_conn, svsk->sk_close); + + if (svsk->sk_close) { + svc_delete_socket(svsk); + return 0; + } + + if (svsk->sk_conn) { + svc_tcp_accept(svsk); + svc_sock_accepted(svsk); + return 0; + } + + ready = svsk->sk_data; + + /* Receive data. If we haven't got the record length yet, get + * the next four bytes. Otherwise try to gobble up as much as + * possible up to the complete record length. + */ + if (svsk->sk_tcplen < 4) { + unsigned long want = 4 - svsk->sk_tcplen; + struct iovec iov; + + iov.iov_base = ((u32 *) &svsk->sk_reclen) + svsk->sk_tcplen; + iov.iov_len = want; + if ((len = svc_recvfrom(rqstp, &iov, 1, want)) < 0) + goto error; + svsk->sk_tcplen += len; + + svsk->sk_reclen = ntohl(svsk->sk_reclen); + if (!(svsk->sk_reclen & 0x80000000)) { + /* FIXME: shutdown socket */ + printk(KERN_NOTICE "RPC: bad TCP reclen %08lx", + (unsigned long) svsk->sk_reclen); + return -EIO; + } + svsk->sk_reclen &= 0x7fffffff; + dprintk("svc: TCP record, %ld bytes\n", svsk->sk_reclen); + } + + /* Check whether enough data is available */ + len = svc_recv_available(svsk); + if (len < 0) + goto error; + + if (len < svsk->sk_reclen) { + dprintk("svc: incomplete TCP record (%d of %ld)\n", + len, svsk->sk_reclen); + svc_sock_received(svsk, ready); + len = -EAGAIN; /* record not complete */ + } + + /* Frob argbuf */ + bufp->iov[0].iov_base += 4; + bufp->iov[0].iov_len -= 4; + + /* Now receive data */ + len = svc_recvfrom(rqstp, bufp->iov, bufp->nriov, svsk->sk_reclen); + if (len < 0) + goto error; + + dprintk("svc: TCP complete record (%d bytes)\n", len); + + /* Position reply write pointer immediately after + * record length */ + rqstp->rq_resbuf.buf += 1; + rqstp->rq_resbuf.len = 1; + + rqstp->rq_skbuff = 0; + rqstp->rq_argbuf.buf += 1; + rqstp->rq_argbuf.len = (len >> 2); + rqstp->rq_prot = IPPROTO_TCP; + + /* Reset TCP read info */ + svsk->sk_reclen = 0; + svsk->sk_tcplen = 0; + + svc_sock_received(svsk, 1); + if (serv->sv_stats) + serv->sv_stats->nettcpcnt++; + + return len; + +error: + if (len == -EAGAIN) { + dprintk("RPC: TCP recvfrom got EAGAIN\n"); + svc_sock_received(svsk, ready); /* Clear data ready */ + } else { + printk(KERN_NOTICE "%s: recvfrom returned errno %d\n", + svsk->sk_server->sv_name, -len); + svc_sock_received(svsk, 0); + } + + return len; +} + +/* + * Send out data on TCP socket. + * FIXME: Make the sendto call non-blocking in order not to hang + * a daemon on a a dead client. Requires write queue maintenance. + */ +static int +svc_tcp_sendto(struct svc_rqst *rqstp) +{ + struct svc_buf *bufp = &rqstp->rq_resbuf; + + /* Set up the first element of the reply iovec. + * Any other iovecs that may be in use have been taken + * care of by the server implementation itself. + */ + bufp->iov[0].iov_base = bufp->base; + bufp->iov[0].iov_len = bufp->len << 2; + bufp->base[0] = htonl(0x80000000|((bufp->len << 2) - 4)); + + return svc_sendto(rqstp, bufp->iov, bufp->nriov); +} + +static int +svc_tcp_init(struct svc_sock *svsk) +{ + struct sock *sk = svsk->sk_sk; + + svsk->sk_recvfrom = svc_tcp_recvfrom; + svsk->sk_sendto = svc_tcp_sendto; + + if (sk->state == TCP_LISTEN) { + dprintk("setting up TCP socket for listening\n"); + sk->state_change = svc_tcp_state_change1; + } else { + dprintk("setting up TCP socket for reading\n"); + sk->state_change = svc_tcp_state_change2; + sk->data_ready = svc_tcp_data_ready; + + svsk->sk_reclen = 0; + svsk->sk_tcplen = 0; + } + + return 0; +} + +/* + * Receive the next request on any socket. + */ +int +svc_recv(struct svc_serv *serv, struct svc_rqst *rqstp) +{ + struct wait_queue wait = { current, NULL }; + struct svc_sock *svsk; + int len; + + dprintk("svc: server %p waiting for data (to = %ld)\n", + rqstp, current->timeout); + +again: + /* Initialize the buffers */ + rqstp->rq_argbuf = rqstp->rq_defbuf; + rqstp->rq_resbuf = rqstp->rq_defbuf; + + if (signalled()) + return -EINTR; + + disable_bh(NET_BH); + if ((svsk = svc_sock_dequeue(serv)) != NULL) { + enable_bh(NET_BH); + rqstp->rq_sock = svsk; + svsk->sk_inuse++; + } else { + /* No data pending. Go to sleep */ + rqstp->rq_sock = NULL; + rqstp->rq_wait = NULL; + svc_serv_enqueue(serv, rqstp); + + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&rqstp->rq_wait, &wait); + enable_bh(NET_BH); + schedule(); + + if (!(svsk = rqstp->rq_sock)) { + svc_serv_dequeue(serv, rqstp); + if (!(svsk = rqstp->rq_sock)) + return signalled()? -EINTR : -EAGAIN; + } + } + + dprintk("svc: server %p servicing socket %p\n", rqstp, svsk); + len = svsk->sk_recvfrom(rqstp); + + /* No data, incomplete (TCP) read, or accept() */ + if (len == 0 || len == -EAGAIN) { + svc_sock_release(rqstp); + goto again; + } + + rqstp->rq_secure = ntohs(rqstp->rq_addr.sin_port) < 1024; + rqstp->rq_userset = 0; + rqstp->rq_verfed = 0; + + svc_getlong(&rqstp->rq_argbuf, rqstp->rq_xid); + svc_putlong(&rqstp->rq_resbuf, rqstp->rq_xid); + + /* Assume that the reply consists of a single buffer. */ + rqstp->rq_resbuf.nriov = 1; + + if (serv->sv_stats) + serv->sv_stats->netcnt++; + return len; +} + +/* + * Drop request + */ +void +svc_drop(struct svc_rqst *rqstp) +{ + dprintk("svc: socket %p dropped request\n", rqstp->rq_sock); + svc_sock_release(rqstp); +} + +/* + * Return reply to client. + */ +int +svc_send(struct svc_rqst *rqstp) +{ + struct svc_sock *svsk; + int len; + + if ((svsk = rqstp->rq_sock) == NULL) { + printk(KERN_WARNING "NULL socket pointer in %s:%d\n", + __FILE__, __LINE__); + return -EFAULT; + } + + /* release the receive skb before sending the reply */ + svc_release_skb(rqstp); + + len = svsk->sk_sendto(rqstp); + svc_sock_release(rqstp); + + if (len == -ECONNREFUSED || len == -ENOTCONN || len == -EAGAIN) + return 0; + return len; +} + +/* + * Initialize socket for RPC use and create svc_sock struct + * XXX: May want to setsockopt SO_SNDBUF and SO_RCVBUF. + */ +static struct svc_sock * +svc_setup_socket(struct svc_serv *serv, struct socket *sock, + int *errp, int pmap_register) +{ + struct svc_sock *svsk; + struct sock *inet; + + dprintk("svc: svc_setup_socket %p\n", sock); + if (!(svsk = kmalloc(sizeof(*svsk), GFP_KERNEL))) { + *errp = -ENOMEM; + return NULL; + } + memset(svsk, 0, sizeof(*svsk)); + +#if LINUX_VERSION_CODE >= 0x020100 + inet = sock->sk; +#else + inet = (struct sock *) sock->data; +#endif + inet->user_data = svsk; + svsk->sk_sock = sock; + svsk->sk_sk = inet; + svsk->sk_ostate = inet->state_change; + svsk->sk_odata = inet->data_ready; + svsk->sk_server = serv; + + /* Initialize the socket */ + if (sock->type == SOCK_DGRAM) + *errp = svc_udp_init(svsk); + else + *errp = svc_tcp_init(svsk); +if (svsk->sk_sk == NULL) + printk(KERN_WARNING "svsk->sk_sk == NULL after svc_prot_init!\n"); + + /* Register socket with portmapper */ + if (*errp >= 0 && pmap_register) + *errp = svc_register(serv, inet->protocol, + ntohs(inet->dummy_th.source)); + + if (*errp < 0) { + inet->user_data = NULL; + kfree(svsk); + return NULL; + } + + svsk->sk_list = serv->sv_allsocks; + serv->sv_allsocks = svsk; + + dprintk("svc: svc_setup_socket created %p (inet %p)\n", + svsk, svsk->sk_sk); + return svsk; +} + +/* + * Create socket for RPC service. + */ +int +svc_create_socket(struct svc_serv *serv, int protocol, struct sockaddr_in *sin) +{ + struct svc_sock *svsk; + struct socket *sock; + int error; + int type; + + dprintk("svc: svc_create_socket(%s, %d, %08lx:%d)\n", + serv->sv_program->pg_name, protocol, + ntohl(sin->sin_addr.s_addr), + ntohs(sin->sin_port)); + + if (protocol != IPPROTO_UDP && protocol != IPPROTO_TCP) { + printk(KERN_WARNING "svc: only UDP and TCP " + "sockets supported\n"); + return -EINVAL; + } + type = (protocol == IPPROTO_UDP)? SOCK_DGRAM : SOCK_STREAM; + + if ((error = sock_create(AF_INET, type, protocol, &sock)) < 0) + return error; + + if (sin != NULL) { + error = sock->ops->bind(sock, (struct sockaddr *) sin, + sizeof(*sin)); + if (error < 0) + goto bummer; + } + + if (protocol == IPPROTO_TCP) { + if ((error = sock->ops->listen(sock, 5)) < 0) + goto bummer; + sock->flags |= SO_ACCEPTCON; + } + + if ((svsk = svc_setup_socket(serv, sock, &error, 1)) != NULL) + return 0; + +bummer: + dprintk("svc: svc_create_socket error = %d\n", -error); + sock_release(sock); + return error; +} + +/* + * Remove a dead socket + */ +void +svc_delete_socket(struct svc_sock *svsk) +{ + struct svc_sock **rsk; + struct svc_serv *serv; + struct sock *sk; + + dprintk("svc: svc_delete_socket(%p)\n", svsk); + + serv = svsk->sk_server; + sk = svsk->sk_sk; + + sk->state_change = svsk->sk_ostate; + sk->data_ready = svsk->sk_odata; + + for (rsk = &serv->sv_allsocks; *rsk; rsk = &(*rsk)->sk_list) { + if (*rsk == svsk) + break; + } + if (!*rsk) + return; + *rsk = svsk->sk_list; + + if (svsk->sk_qued) + rpc_remove_list(&serv->sv_sockets, svsk); + svsk->sk_dead = 1; + + if (!svsk->sk_inuse) { + sock_release(svsk->sk_sock); + kfree(svsk); + } else { + printk(KERN_NOTICE "svc: server socket destroy delayed\n"); + svsk->sk_server = NULL; + } +} diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/sysctl.c linux/net/sunrpc/sysctl.c --- v2.1.31/linux/net/sunrpc/sysctl.c Wed Dec 31 16:00:00 1969 +++ linux/net/sunrpc/sysctl.c Mon Mar 3 00:55:59 1997 @@ -0,0 +1,134 @@ +/* + * linux/net/sunrpc/sysctl.c + * + * Sysctl interface to sunrpc module. This is for debugging only now. + * + * I would prefer to register the sunrpc table below sys/net, but that's + * impossible at the moment. + */ + +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= 0x020100 +#include +#else +# include +# define copy_from_user memcpy_fromfs +# define copy_to_user memcpy_tofs +# define access_ok !verify_area +#endif +#include + +/* + * Declare the debug flags here + */ +unsigned int rpc_debug = 0; +unsigned int nfs_debug = 0; +unsigned int nfsd_debug = 0; +unsigned int nlm_debug = 0; + +#ifdef RPC_DEBUG + +static struct ctl_table_header *sunrpc_table_header = NULL; +static ctl_table sunrpc_table[]; + +void +rpc_register_sysctl(void) +{ + if (sunrpc_table_header) + return; + sunrpc_table_header = register_sysctl_table(sunrpc_table, 1); +} + +void +rpc_unregister_sysctl(void) +{ + if (!sunrpc_table_header) + return; + unregister_sysctl_table(sunrpc_table_header); +} + +int +proc_dodebug(ctl_table *table, int write, struct file *file, + void *buffer, size_t *lenp) +{ + char tmpbuf[20], *p, c; + unsigned int value; + int left, len; + + if ((file->f_pos && !write) || !*lenp) { + *lenp = 0; + return 0; + } + + left = *lenp; + + if (write) { + if (!access_ok(VERIFY_READ, buffer, left)) + return -EFAULT; + p = (char *) buffer; +#if LINUX_VERSION_CODE >= 0x020100 + while (left && __get_user(c, p) >= 0 && isspace(c)) + left--, p++; +#else + while (left && (c = get_fs_byte(p)) >= 0 && isspace(c)) + left--, p++; +#endif + if (!left) + goto done; + + if (left > sizeof(tmpbuf) - 1) + return -EINVAL; + copy_from_user(tmpbuf, p, left); + tmpbuf[left] = '\0'; + + for (p = tmpbuf, value = 0; '0' <= *p && *p <= '9'; p++, left--) + value = 10 * value + (*p - '0'); + if (*p && !isspace(*p)) + return -EINVAL; + while (left && isspace(*p)) + left--, p++; + *(unsigned int *) table->data = value; + } else { + if (!access_ok(VERIFY_WRITE, buffer, left)) + return -EFAULT; + len = sprintf(tmpbuf, "%d", *(unsigned int *) table->data); + if (len > left) + len = left; + copy_to_user(buffer, tmpbuf, len); + if ((left -= len) > 0) { + put_user('\n', (char *)buffer + len); + left--; + } + } + +done: + *lenp -= left; + file->f_pos += *lenp; + return 0; +} + +#define DIRENTRY(nam1, nam2, child) \ + {CTL_##nam1, #nam2, NULL, 0, 0555, child } +#define DBGENTRY(nam1, nam2) \ + {CTL_##nam1##DEBUG, #nam2 "_debug", &nam2##_debug, sizeof(int),\ + 0644, NULL, &proc_dodebug} + +static ctl_table debug_table[] = { + DBGENTRY(RPC, rpc), + DBGENTRY(NFS, nfs), + DBGENTRY(NFSD, nfsd), + DBGENTRY(NLM, nlm), + {0} +}; + +static ctl_table sunrpc_table[] = { + DIRENTRY(SUNRPC, sunrpc, debug_table), + {0} +}; + +#endif diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/xdr.c linux/net/sunrpc/xdr.c --- v2.1.31/linux/net/sunrpc/xdr.c Wed Dec 31 16:00:00 1969 +++ linux/net/sunrpc/xdr.c Thu Dec 19 09:04:19 1996 @@ -0,0 +1,118 @@ +/* + * linux/net/sunrpc/xdr.c + * + * Generic XDR support. + * + * Copyright (C) 1995, 1996 Olaf Kirch + */ + +#include +#include +#include +#include +#include +#include + +u32 rpc_success, rpc_prog_unavail, rpc_prog_mismatch, rpc_proc_unavail, + rpc_garbage_args, rpc_system_err; +u32 rpc_auth_ok, rpc_autherr_badcred, rpc_autherr_rejectedcred, + rpc_autherr_badverf, rpc_autherr_rejectedverf, rpc_autherr_tooweak; +u32 xdr_zero, xdr_one, xdr_two; + +void +xdr_init(void) +{ + static int inited = 0; + + if (inited) + return; + + xdr_zero = htonl(0); + xdr_one = htonl(1); + xdr_two = htonl(2); + + rpc_success = htonl(RPC_SUCCESS); + rpc_prog_unavail = htonl(RPC_PROG_UNAVAIL); + rpc_prog_mismatch = htonl(RPC_PROG_MISMATCH); + rpc_proc_unavail = htonl(RPC_PROC_UNAVAIL); + rpc_garbage_args = htonl(RPC_GARBAGE_ARGS); + rpc_system_err = htonl(RPC_SYSTEM_ERR); + + rpc_auth_ok = htonl(RPC_AUTH_OK); + rpc_autherr_badcred = htonl(RPC_AUTH_BADCRED); + rpc_autherr_rejectedcred = htonl(RPC_AUTH_REJECTEDCRED); + rpc_autherr_badverf = htonl(RPC_AUTH_BADVERF); + rpc_autherr_rejectedverf = htonl(RPC_AUTH_REJECTEDVERF); + rpc_autherr_tooweak = htonl(RPC_AUTH_TOOWEAK); + + inited = 1; +} + +/* + * XDR functions for basic NFS types + */ +u32 * +xdr_encode_netobj(u32 *p, const struct xdr_netobj *obj) +{ + unsigned int quadlen = XDR_QUADLEN(obj->len); + + *p++ = htonl(obj->len); + p[quadlen-1] = 0; /* zero trailing bytes */ + memcpy(p, obj->data, obj->len); + return p + XDR_QUADLEN(obj->len); +} + +u32 * +xdr_decode_netobj_fixed(u32 *p, void *obj, unsigned int len) +{ + if (ntohl(*p++) != len) + return NULL; + memcpy(obj, p, len); + return p + XDR_QUADLEN(len); +} + +u32 * +xdr_decode_netobj(u32 *p, struct xdr_netobj *obj) +{ + unsigned int len; + + if ((len = ntohl(*p++)) > XDR_MAX_NETOBJ) + return NULL; + obj->len = len; + obj->data = (u8 *) p; + return p + XDR_QUADLEN(len); +} + +u32 * +xdr_encode_string(u32 *p, const char *string) +{ + int len = strlen(string); + int quadlen = XDR_QUADLEN(len); + + p[quadlen] = 0; + *p++ = htonl(len); + memcpy(p, string, len); + return p + quadlen; +} + +u32 * +xdr_decode_string(u32 *p, char **sp, int *lenp, int maxlen) +{ + unsigned int len; + char *string; + + if ((len = ntohl(*p++)) > maxlen) + return NULL; + if (lenp) + *lenp = len; + if ((len % 4) != 0) { + string = (char *) p; + } else { + string = (char *) (p - 1); + memmove(string, p, len); + } + string[len] = '\0'; + *sp = string; + return p + XDR_QUADLEN(len); +} + diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/xprt.c linux/net/sunrpc/xprt.c --- v2.1.31/linux/net/sunrpc/xprt.c Wed Dec 31 16:00:00 1969 +++ linux/net/sunrpc/xprt.c Thu Mar 20 07:52:08 1997 @@ -0,0 +1,1367 @@ +/* + * linux/net/sunrpc/xprt.c + * + * This is a generic RPC call interface supporting congestion avoidance, + * and asynchronous calls. + * + * The interface works like this: + * + * - When a process places a call, it allocates a request slot if + * one is available. Otherwise, it sleeps on the backlog queue + * (xprt_reserve). + * - Next, the caller puts together the RPC message, stuffs it into + * the request struct, and calls xprt_call(). + * - xprt_call transmits the message and installs the caller on the + * socket's wait list. At the same time, it installs a timer that + * is run after the packet's timeout has expired. + * - When a packet arrives, the data_ready handler walks the list of + * pending requests for that socket. If a matching XID is found, the + * caller is woken up, and the timer removed. + * - When no reply arrives within the timeout interval, the timer is + * fired by the kernel and runs xprt_timer(). It either adjusts the + * timeout values (minor timeout) or wakes up the caller with a status + * of -ETIMEDOUT. + * - When the caller receives a notification from RPC that a reply arrived, + * it should release the RPC slot, and process the reply. + * If the call timed out, it may choose to retry the operation by + * adjusting the initial timeout value, and simply calling rpc_call + * again. + * + * Support for async RPC is done through a set of RPC-specific scheduling + * primitives that `transparently' work for processes as well as async + * tasks that rely on callbacks. + * + * Copyright (C) 1995, 1996, Olaf Kirch + */ + +#define __KERNEL_SYSCALLS__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if LINUX_VERSION_CODE >= 0x020100 +#include +#endif + +#define SOCK_HAS_USER_DATA + +/* + * Local variables + */ +#ifndef SOCK_HAS_USER_DATA +static struct rpc_xprt * sock_list = NULL; +#endif + +#ifdef RPC_DEBUG +# undef RPC_DEBUG_DATA +# define RPCDBG_FACILITY RPCDBG_XPRT +#endif + +#ifndef MAX +# define MAX(a, b) ((a) > (b)? (a) : (b)) +# define MIN(a, b) ((a) < (b)? (a) : (b)) +#endif + +/* + * Local functions + */ +static void xprt_request_init(struct rpc_task *, struct rpc_xprt *); +static void xprt_transmit_status(struct rpc_task *task); +static void xprt_receive_status(struct rpc_task *task); +static void xprt_reserve_status(struct rpc_task *task); +static void xprt_reconn_timeout(struct rpc_task *task); +static void xprt_reconn_status(struct rpc_task *task); +static struct socket *xprt_create_socket(int, struct sockaddr_in *, + struct rpc_timeout *); + +#ifdef RPC_DEBUG_DATA +/* + * Print the buffer contents (first 128 bytes only--just enough for + * diropres return). + */ +static void +xprt_pktdump(char *msg, u32 *packet, unsigned int count) +{ + u8 *buf = (u8 *) packet; + int j; + + dprintk("RPC: %s\n", msg); + for (j = 0; j < count && j < 128; j += 4) { + if (!(j & 31)) { + if (j) + dprintk("\n"); + dprintk("0x%04x ", j); + } + dprintk("%02x%02x%02x%02x ", + buf[j], buf[j+1], buf[j+2], buf[j+3]); + } + dprintk("\n"); +} +#else +static inline void +xprt_pktdump(char *msg, u32 *packet, unsigned int count) +{ + /* NOP */ +} +#endif + +/* + * Look up RPC transport given an INET socket + */ +static inline struct rpc_xprt * +xprt_from_sock(struct sock *sk) +{ +#ifndef SOCK_HAS_USER_DATA + struct rpc_xprt *xprt; + + for (xprt = sock_list; xprt && sk != xprt->inet; xprt = xprt->link) + ; + return xprt; +#else + return (struct rpc_xprt *) sk->user_data; +#endif +} + +/* + * Write data to socket. + */ +static inline int +xprt_sendmsg(struct rpc_xprt *xprt) +{ + struct socket *sock = xprt->sock; + struct msghdr msg; + unsigned long oldfs; + int result; + + xprt_pktdump("packet data:", + xprt->snd_buf.io_vec->iov_base, + xprt->snd_buf.io_vec->iov_len); + +#if LINUX_VERSION_CODE >= 0x020100 + msg.msg_flags = MSG_DONTWAIT; + msg.msg_iov = xprt->snd_buf.io_vec; + msg.msg_iovlen = xprt->snd_buf.io_nr; + msg.msg_name = (struct sockaddr *) &xprt->addr; + msg.msg_namelen = sizeof(xprt->addr); + msg.msg_control = NULL; + + oldfs = get_fs(); set_fs(get_ds()); + result = sock_sendmsg(sock, &msg, xprt->snd_buf.io_len); + set_fs(oldfs); +#else + msg.msg_flags = 0; + msg.msg_iov = xprt->snd_buf.io_vec; + msg.msg_iovlen = xprt->snd_buf.io_nr; + msg.msg_name = (struct sockaddr *) &xprt->addr; + msg.msg_namelen = sizeof(xprt->addr); + msg.msg_control = NULL; + + oldfs = get_fs(); set_fs(get_ds()); + result = sock->ops->sendmsg(sock, &msg, xprt->snd_buf.io_len, 1, 0); + set_fs(oldfs); +#endif + + dprintk("RPC: xprt_sendmsg(%d) = %d\n", + xprt->snd_buf.io_len, result); + + if (result >= 0) { + xprt->snd_buf.io_len -= result; + return result; + } + + switch (result) { + case -ECONNREFUSED: + /* When the server has died, an ICMP port unreachable message + * prompts ECONNREFUSED. + */ + break; + case -ENOTCONN: case -EPIPE: + /* connection broken */ + break; + default: + printk(KERN_NOTICE "RPC: sendmsg returned error %d\n", -result); + result = 0; + } + return result; +} + +/* + * Read data from socket + */ +static inline int +xprt_recvmsg(struct rpc_xprt *xprt, struct iovec *iov, int nr, int len) +{ + struct socket *sock = xprt->sock; + struct sockaddr_in sin; + struct msghdr msg; + unsigned long oldfs; + int result; + +#if LINUX_VERSION_CODE >= 0x020100 + msg.msg_flags = MSG_DONTWAIT; + msg.msg_iov = iov; + msg.msg_iovlen = nr; + msg.msg_name = &sin; + msg.msg_namelen = sizeof(sin); + msg.msg_control = NULL; + + oldfs = get_fs(); set_fs(get_ds()); + result = sock_recvmsg(sock, &msg, len, MSG_DONTWAIT); + set_fs(oldfs); +#else + int alen = sizeof(sin); + msg.msg_flags = 0; + msg.msg_iov = iov; + msg.msg_iovlen = nr; + msg.msg_name = &sin; + msg.msg_namelen = sizeof(sin); + msg.msg_control = NULL; + + oldfs = get_fs(); set_fs(get_ds()); + result = sock->ops->recvmsg(sock, &msg, len, 1, 0, &alen); + set_fs(oldfs); +#endif + + if (!result && len) + result = -EAGAIN; + + dprintk("RPC: xprt_recvmsg(iov %p, len %d) = %d\n", + iov, len, result); + return result; +} + + +/* + * Adjust RPC congestion window + * We use a time-smoothed congestion estimator to avoid heavy oscillation. + */ +static void +xprt_adjust_cwnd(struct rpc_xprt *xprt, int result) +{ + unsigned long cwnd = xprt->cwnd; + + if (xprt->nocong) + return; + if (result >= 0) { + if (xprt->cong < cwnd || jiffies < xprt->congtime) + return; + /* The (cwnd >> 1) term makes sure + * the result gets rounded properly. */ + cwnd += (RPC_CWNDSCALE * RPC_CWNDSCALE + (cwnd >> 1)) / cwnd; + if (cwnd > RPC_MAXCWND) + cwnd = RPC_MAXCWND; + else + pprintk("RPC: %lu %ld cwnd\n", jiffies, cwnd); + xprt->congtime = jiffies + ((cwnd * HZ) << 2) / RPC_CWNDSCALE; + dprintk("RPC: cong %08lx, cwnd was %08lx, now %08lx, " + "time %ld ms\n", xprt->cong, xprt->cwnd, cwnd, + (xprt->congtime-jiffies)*1000/HZ); + } else if (result == -ETIMEDOUT) { + if ((cwnd >>= 1) < RPC_CWNDSCALE) + cwnd = RPC_CWNDSCALE; + xprt->congtime = jiffies + ((cwnd * HZ) << 3) / RPC_CWNDSCALE; + dprintk("RPC: cong %08lx, cwnd was %08lx, now %08lx, " + "time %ld ms\n", xprt->cong, xprt->cwnd, cwnd, + (xprt->congtime-jiffies)*1000/HZ); + pprintk("RPC: %lu %ld cwnd\n", jiffies, cwnd); + } + + xprt->cwnd = cwnd; +} + +/* + * Adjust timeout values etc for next retransmit + */ +int +xprt_adjust_timeout(struct rpc_timeout *to) +{ + if (to->to_exponential) + to->to_current <<= 1; + else + to->to_current += to->to_increment; + if (to->to_maxval && to->to_current >= to->to_maxval) { + to->to_current = to->to_maxval; + to->to_retries = 0; + } + if (!to->to_current) { + printk(KERN_WARNING "xprt_adjust_timeout: to_current = 0!\n"); + to->to_current = 5 * HZ; + } + pprintk("RPC: %lu %s\n", jiffies, + to->to_retries? "retrans" : "timeout"); + return (to->to_retries)--; +} + +/* + * Close down a transport socket + */ +static void +xprt_close(struct rpc_xprt *xprt) +{ + struct sock *sk = xprt->inet; + +#ifdef SOCK_HAS_USER_DATA + sk->user_data = NULL; +#endif + sk->data_ready = xprt->old_data_ready; + sk->state_change = xprt->old_state_change; + sk->write_space = xprt->old_write_space; + + if (xprt->file) + close_fp(xprt->file); + else + sock_release(xprt->sock); +} + +/* + * Mark a transport as disconnected + */ +static void +xprt_disconnect(struct rpc_xprt *xprt) +{ + dprintk("RPC: disconnected transport %p\n", xprt); + rpc_wake_up_status(&xprt->pending, -ENOTCONN); + rpc_wake_up_status(&xprt->sending, -ENOTCONN); + xprt->connected = 0; +} + +/* + * Reconnect a broken TCP connection. + */ +void +xprt_reconnect(struct rpc_task *task) +{ + struct rpc_xprt *xprt = task->tk_xprt; + struct socket *sock; + struct sock *inet; + int status; + + dprintk("RPC: %4d xprt_reconnect %p connected %d\n", + task->tk_pid, xprt, xprt->connected); + task->tk_status = 0; + + if (xprt->connecting) { + task->tk_timeout = xprt->timeout.to_maxval; + rpc_sleep_on(&xprt->reconn, task, NULL, NULL); + return; + } + xprt->connecting = 1; + + /* Create an unconnected socket */ + if (!(sock = xprt_create_socket(xprt->prot, NULL, &xprt->timeout))) + goto defer; + +#if LINUX_VERSION_CODE >= 0x020100 + inet = sock->sk; +#else + inet = (struct sock *) sock->data; +#endif + inet->data_ready = xprt->inet->data_ready; + inet->state_change = xprt->inet->state_change; + inet->write_space = xprt->inet->write_space; +#ifdef SOCK_HAS_USER_DATA + inet->user_data = xprt; +#endif + + dprintk("RPC: %4d closing old socket\n", task->tk_pid); + xprt_disconnect(xprt); + xprt_close(xprt); + + /* Reset to new socket and default congestion */ + xprt->sock = sock; + xprt->inet = inet; + xprt->cwnd = RPC_INITCWND; + + /* Now connect it asynchronously. */ + dprintk("RPC: %4d connecting new socket\n", task->tk_pid); + status = sock->ops->connect(sock, (struct sockaddr *) &xprt->addr, + sizeof(xprt->addr), O_NONBLOCK); + if (status < 0) { + if (status != -EINPROGRESS && status != -EALREADY) { + printk("RPC: TCP connect error %d!\n", -status); + goto defer; + } + + dprintk("RPC: %4d connect status %d connected %d\n", + task->tk_pid, status, xprt->connected); + task->tk_timeout = 60 * HZ; + + disable_bh(NET_BH); + if (!xprt->connected) { + rpc_sleep_on(&xprt->reconn, task, + xprt_reconn_status, xprt_reconn_timeout); + enable_bh(NET_BH); + return; + } + enable_bh(NET_BH); + } + + xprt->connecting = 0; + rpc_wake_up(&xprt->reconn); + return; + +defer: + task->tk_timeout = 30 * HZ; + rpc_sleep_on(&xprt->reconn, task, NULL, NULL); + xprt->connecting = 0; +} + +/* + * Reconnect status + */ +static void +xprt_reconn_status(struct rpc_task *task) +{ + struct rpc_xprt *xprt = task->tk_xprt; + + dprintk("RPC: %4d xprt_reconn_status %d\n", + task->tk_pid, task->tk_status); + if (!xprt->connected && task->tk_status != -ETIMEDOUT) { + task->tk_timeout = 30 * HZ; + rpc_sleep_on(&xprt->reconn, task, NULL, xprt_reconn_timeout); + } +} + +/* + * Reconnect timeout. We just mark the transport as not being in the + * process of reconnecting, and leave the rest to the upper layers. + */ +static void +xprt_reconn_timeout(struct rpc_task *task) +{ + dprintk("RPC: %4d xprt_reconn_timeout %d\n", + task->tk_pid, task->tk_status); + task->tk_status = -ENOTCONN; + task->tk_xprt->connecting = 0; + task->tk_timeout = 0; + rpc_wake_up_task(task); +} + +/* + * Look up the RPC request corresponding to a reply. + */ +static inline struct rpc_rqst * +xprt_lookup_rqst(struct rpc_xprt *xprt, u32 xid) +{ + struct rpc_task *head, *task; + struct rpc_rqst *req; + int safe = 0; + + if ((head = xprt->pending.task) != NULL) { + task = head; + do { + if ((req = task->tk_rqstp) && req->rq_xid == xid) + return req; + task = task->tk_next; + if (++safe > 100) { + printk("xprt_lookup_rqst: loop in Q!\n"); + return NULL; + } + } while (task != head); + } + dprintk("RPC: unknown XID %08x in reply.\n", xid); + return NULL; +} + +/* + * Complete reply received. + * The TCP code relies on us to remove the request from xprt->pending. + */ +static inline void +xprt_complete_rqst(struct rpc_xprt *xprt, struct rpc_rqst *req, int copied) +{ + struct rpc_task *task = req->rq_task; + + req->rq_rlen = copied; + req->rq_gotit = 1; + + /* Adjust congestion window */ + xprt_adjust_cwnd(xprt, copied); + +#ifdef RPC_PROFILE + /* Profile only reads for now */ + if (copied > 1024) { + static unsigned long nextstat = 0; + static unsigned long pkt_rtt = 0, pkt_len = 0, pkt_cnt = 0; + + pkt_cnt++; + pkt_len += req->rq_slen + copied; + pkt_rtt += jiffies - req->rq_xtime; + if (nextstat < jiffies) { + printk("RPC: %lu %ld cwnd\n", jiffies, xprt->cwnd); + printk("RPC: %ld %ld %ld %ld stat\n", + jiffies, pkt_cnt, pkt_len, pkt_rtt); + pkt_rtt = pkt_len = pkt_cnt = 0; + nextstat = jiffies + 5 * HZ; + } + } +#endif + + /* ... and wake up the process. */ + dprintk("RPC: %4d has input (%d bytes)\n", task->tk_pid, copied); + task->tk_status = copied; + + rpc_wake_up_task(task); + return; +} + +/* + * Input handler for RPC replies. Called from a bottom half and hence + * atomic. + */ +static inline void +udp_data_ready(struct sock *sk, int len) +{ + struct rpc_task *task; + struct rpc_xprt *xprt; + struct rpc_rqst *rovr; + struct sk_buff *skb; + struct iovec iov[MAX_IOVEC]; + unsigned long oldfs; + int err, repsize, copied; + + dprintk("RPC: udp_data_ready...\n"); + if (!(xprt = xprt_from_sock(sk))) + return; + dprintk("RPC: udp_data_ready client %p\n", xprt); + + if ((skb = skb_recv_datagram(sk, 0, 1, &err)) == NULL) + return; + repsize = skb->len - 8; /* don't account for UDP header */ + + if (repsize < 4) { + printk("RPC: impossible RPC reply size %d!\n", repsize); + goto dropit; + } + + /* Look up the request corresponding to the given XID */ + if (!(rovr = xprt_lookup_rqst(xprt, *(u32 *) (skb->h.raw + 8)))) + goto dropit; + task = rovr->rq_task; + + dprintk("RPC: %4d received reply\n", task->tk_pid); + xprt_pktdump("packet data:", (u32 *) (skb->h.raw+8), repsize); + + if ((copied = rovr->rq_rlen) > repsize) + copied = repsize; + + /* Okay, we have it. Copy datagram... */ + memcpy(iov, rovr->rq_rvec, rovr->rq_rnr * sizeof(iov[0])); + oldfs = get_fs(); set_fs(get_ds()); + skb_copy_datagram_iovec(skb, 8, iov, copied); + set_fs(oldfs); + + xprt_complete_rqst(xprt, rovr, copied); + +dropit: + skb_free_datagram(sk, skb); + return; +} + +/* + * TCP record receive routine + * This is not the most efficient code since we call recvfrom twice-- + * first receiving the record marker and XID, then the data. + * + * The optimal solution would be a RPC support in the TCP layer, which + * would gather all data up to the next record marker and then pass us + * the list of all TCP segments ready to be copied. + */ +static inline int +tcp_input_record(struct rpc_xprt *xprt) +{ + struct rpc_rqst *req; + struct iovec *iov; + struct iovec riov; + u32 offset; + int result, maxcpy, reclen, avail, want; + + dprintk("RPC: tcp_input_record\n"); + offset = xprt->tcp_offset; + result = -EAGAIN; + if (offset < 4 || (!xprt->tcp_more && offset < 8)) { + want = (xprt->tcp_more? 4 : 8) - offset; + dprintk("RPC: reading header (%d bytes)\n", want); + riov.iov_base = xprt->tcp_recm.data + offset; + riov.iov_len = want; + result = xprt_recvmsg(xprt, &riov, 1, want); + if (result < 0) + goto done; + offset += result; + if (result < want) { + result = -EAGAIN; + goto done; + } + + /* Get the record length and mask out the more_fragments bit */ + reclen = ntohl(xprt->tcp_reclen); + dprintk("RPC: reclen %08x\n", reclen); + xprt->tcp_more = (reclen & 0x80000000)? 0 : 1; + if (!(reclen &= 0x7fffffff)) { + printk(KERN_NOTICE "RPC: empty TCP record.\n"); + return -ENOTCONN; /* break connection */ + } + xprt->tcp_total += reclen; + xprt->tcp_reclen = reclen; + + dprintk("RPC: got xid %08x reclen %d morefrags %d\n", + xprt->tcp_xid, xprt->tcp_reclen, xprt->tcp_more); + if (!xprt->tcp_copied + && (req = xprt_lookup_rqst(xprt, xprt->tcp_xid))) { + iov = xprt->tcp_iovec; + memcpy(iov, req->rq_rvec, req->rq_rnr * sizeof(iov[0])); +#if 0 +*(u32 *)iov->iov_base = req->rq_xid; +#endif + iov->iov_base += 4; + iov->iov_len -= 4; + xprt->tcp_copied = 4; + xprt->tcp_rqstp = req; + } + } else { + reclen = xprt->tcp_reclen; + } + + avail = reclen - (offset - 4); + if ((req = xprt->tcp_rqstp) && req->rq_xid == xprt->tcp_xid + && req->rq_task->tk_rpcwait == &xprt->pending) { + want = MIN(req->rq_rlen - xprt->tcp_copied, avail); + + dprintk("RPC: %4d TCP receiving %d bytes\n", + req->rq_task->tk_pid, want); + result = xprt_recvmsg(xprt, xprt->tcp_iovec, req->rq_rnr, want); + if (result < 0) + goto done; + xprt->tcp_copied += result; + offset += result; + avail -= result; + if (result < want) { + result = -EAGAIN; + goto done; + } + + maxcpy = MIN(req->rq_rlen, xprt->tcp_total); + if (xprt->tcp_copied == maxcpy && !xprt->tcp_more) { + dprintk("RPC: %4d received reply complete\n", + req->rq_task->tk_pid); + xprt_complete_rqst(xprt, req, xprt->tcp_total); + xprt->tcp_copied = 0; + xprt->tcp_rqstp = NULL; + } + /* Request must be re-encoded before retransmit */ + req->rq_damaged = 1; + } + + /* Skip over any trailing bytes on short reads */ + while (avail) { + static u8 dummy[64]; + + want = MIN(avail, sizeof(dummy)); + riov.iov_base = dummy; + riov.iov_len = want; + dprintk("RPC: TCP skipping %d bytes\n", want); + result = xprt_recvmsg(xprt, &riov, 1, want); + if (result < 0) + goto done; + offset += result; + avail -= result; + if (result < want) { + result = -EAGAIN; + goto done; + } + } + if (!xprt->tcp_more) + xprt->tcp_total = 0; + offset = 0; + +done: + dprintk("RPC: tcp_input_record done (off %d total %d copied %d)\n", + offset, xprt->tcp_total, xprt->tcp_copied); + xprt->tcp_offset = offset; + return result; +} + +/* + * data_ready callback for TCP. + */ +static void +tcp_data_ready(struct sock *sk, int len) +{ + struct rpc_xprt *xprt; + int result, safe_retry = 0; + + dprintk("RPC: tcp_data_ready...\n"); + if (!(xprt = xprt_from_sock(sk))) + return; + dprintk("RPC: tcp_data_ready client %p\n", xprt); + dprintk("RPC: state %x conn %d dead %d zapped %d\n", + sk->state, xprt->connected, + sk->dead, sk->zapped); + + do { + if (safe_retry++ > 20) + return; + result = tcp_input_record(xprt); + } while (result >= 0); + + switch (result) { + case -EAGAIN: + return; + case -ENOTCONN: + case -EPIPE: + xprt_disconnect(xprt); + return; + default: + printk("RPC: unexpected error %d from tcp_input_record\n", + result); + } +} + +static void +tcp_state_change(struct sock *sk) +{ + struct rpc_xprt *xprt; + + if (!(xprt = xprt_from_sock(sk))) + return; + dprintk("RPC: tcp_state_change client %p...\n", xprt); + dprintk("RPC: state %x conn %d dead %d zapped %d\n", + sk->state, xprt->connected, + sk->dead, sk->zapped); + + if (sk->state == TCP_ESTABLISHED && !xprt->connected) { + xprt->connected = 1; + xprt->connecting = 0; + rpc_wake_up(&xprt->reconn); + } else if (sk->zapped) { + rpc_wake_up_status(&xprt->pending, -ENOTCONN); + rpc_wake_up_status(&xprt->sending, -ENOTCONN); + rpc_wake_up_status(&xprt->reconn, -ENOTCONN); + } +} + +static void +tcp_write_space(struct sock *sk) +{ + struct rpc_xprt *xprt; + + if (!(xprt = xprt_from_sock(sk))) + return; + xprt->write_space = 1; + if (xprt->snd_task && !RPC_IS_RUNNING(xprt->snd_task)) + rpc_wake_up_task(xprt->snd_task); +} + +/* + * RPC receive timeout handler. + */ +static void +xprt_timer(struct rpc_task *task) +{ + if (task->tk_rqstp) + xprt_adjust_cwnd(task->tk_xprt, -ETIMEDOUT); + + dprintk("RPC: %4d xprt_timer (%s request)\n", task->tk_pid, + task->tk_rqstp? "pending" : "backlogged"); + + task->tk_status = -ETIMEDOUT; + task->tk_timeout = 0; + rpc_wake_up_task(task); +} + +/* + * (Partly) transmit the RPC packet + * Note that task->tk_status is either 0 or negative on return. + * Only when the reply is received will the status be set to a + * positive value. + */ +static inline int +xprt_transmit_some(struct rpc_xprt *xprt, struct rpc_task *task) +{ + struct rpc_rqst *req = task->tk_rqstp; + int result; + + task->tk_status = 0; + if ((result = xprt_sendmsg(xprt)) >= 0) { + if (!xprt->snd_buf.io_len || !xprt->stream) { + rpc_wake_up_next(&xprt->sending); + return req->rq_slen; + } + result = -EAGAIN; + } else if (xprt->stream) { + if (result == -ENOTCONN || result == -EPIPE) { + xprt_disconnect(xprt); + result = -ENOTCONN; + } + } + return task->tk_status = result; +} + +/* + * Place the actual RPC call. + * We have to copy the iovec because sendmsg fiddles with its contents. + */ +void +xprt_transmit(struct rpc_task *task) +{ + struct rpc_timeout *timeo; + struct rpc_rqst *req = task->tk_rqstp; + struct rpc_xprt *xprt = req->rq_xprt; + + dprintk("RPC: %4d xprt_transmit(%x)\n", task->tk_pid, + *(u32 *)(req->rq_svec[0].iov_base)); + + if (xprt->shutdown) { + task->tk_status = -EIO; + return; + } + + /* If we're not already in the process of transmitting our call, + * set up everything as needed. */ + if (xprt->snd_task != task) { + /* Write the record marker */ + if (xprt->stream) { + u32 marker; + + if (!xprt->connected) { + task->tk_status = -ENOTCONN; + return; + } + marker = htonl(0x80000000|(req->rq_slen-4)); + *((u32 *) req->rq_svec[0].iov_base) = marker; + } + + /* Reset timeout parameters */ + timeo = &req->rq_timeout; + if (timeo->to_retries < 0) { + dprintk("RPC: %4d xprt_transmit reset timeo\n", + task->tk_pid); + timeo->to_retries = xprt->timeout.to_retries; + timeo->to_current = timeo->to_initval; + } + +#ifdef RPC_PROFILE + req->rq_xtime = jiffies; +#endif + req->rq_gotit = 0; + + if (xprt->snd_task) { + dprintk("RPC: %4d TCP write queue full (task %d)\n", + task->tk_pid, xprt->snd_task->tk_pid); + rpc_sleep_on(&xprt->sending, task, + xprt_transmit_status, NULL); + return; + } + xprt->snd_buf = req->rq_snd_buf; + xprt->snd_task = task; + } + + /* For fast networks/servers we have to put the request on + * the pending list now: + */ + disable_bh(NET_BH); + rpc_add_wait_queue(&xprt->pending, task); + task->tk_callback = NULL; + enable_bh(NET_BH); + + /* Continue transmitting the packet/record. We must be careful + * to cope with writespace callbacks arriving _after_ we have + * called xprt_sendmsg(). + */ + while (1) { + xprt->write_space = 0; + if (xprt_transmit_some(xprt, task) != -EAGAIN) { + dprintk("RPC: %4d xmit complete\n", task->tk_pid); + xprt->snd_task = NULL; + return; + } + + dprintk("RPC: %4d xmit incomplete (%d left of %d)\n", + task->tk_pid, xprt->snd_buf.io_len, + req->rq_slen); + task->tk_status = 0; + disable_bh(NET_BH); + if (!xprt->write_space) { + /* Remove from pending */ + rpc_remove_wait_queue(task); + rpc_sleep_on(&xprt->sending, task, + xprt_transmit_status, NULL); + enable_bh(NET_BH); + return; + } + enable_bh(NET_BH); + } +} + +/* + * This callback is invoked when the sending task is forced to sleep + * because the TCP write buffers are full + */ +static void +xprt_transmit_status(struct rpc_task *task) +{ + struct rpc_xprt *xprt = task->tk_client->cl_xprt; + + dprintk("RPC: %4d transmit_status %d\n", task->tk_pid, task->tk_status); + if (xprt->snd_task == task) { + if (task->tk_status < 0) + xprt->snd_task = NULL; + xprt_disconnect(xprt); + } +} + +/* + * Wait for the reply to our call. + * When the callback is invoked, the congestion window should have + * been updated already. + */ +void +xprt_receive(struct rpc_task *task) +{ + struct rpc_rqst *req = task->tk_rqstp; + struct rpc_xprt *xprt = req->rq_xprt; + + dprintk("RPC: %4d xprt_receive\n", task->tk_pid); + if (xprt->connected == 0) { + task->tk_status = -ENOTCONN; + return; + } + + /* + * Wait until rq_gotit goes non-null, or timeout elapsed. + */ + task->tk_timeout = req->rq_timeout.to_current; + + disable_bh(NET_BH); + if (!req->rq_gotit) { + rpc_sleep_on(&xprt->pending, task, + xprt_receive_status, xprt_timer); + } + enable_bh(NET_BH); + + dprintk("RPC: %4d xprt_receive returns %d\n", + task->tk_pid, task->tk_status); +} + +static void +xprt_receive_status(struct rpc_task *task) +{ + struct rpc_xprt *xprt = task->tk_xprt; + + if (xprt->stream && xprt->tcp_rqstp == task->tk_rqstp) + xprt->tcp_rqstp = NULL; +} + +/* + * Reserve an RPC call slot. + */ +int +xprt_reserve(struct rpc_task *task) +{ + struct rpc_xprt *xprt = task->tk_xprt; + + /* We already have an initialized request. */ + if (task->tk_rqstp) + return 0; + + dprintk("RPC: %4d xprt_reserve cong = %ld cwnd = %ld\n", + task->tk_pid, xprt->cong, xprt->cwnd); + if ((!RPCXPRT_CONGESTED(xprt) && xprt->free)) { + xprt_reserve_status(task); + task->tk_timeout = 0; + } else if (!task->tk_timeout) { + task->tk_status = -ENOBUFS; + } else { + dprintk("RPC: xprt_reserve waiting on backlog\n"); + rpc_sleep_on(&xprt->backlog, task, xprt_reserve_status, NULL); + } + dprintk("RPC: %4d xprt_reserve returns %d\n", + task->tk_pid, task->tk_status); + return task->tk_status; +} + +/* + * Reservation callback + */ +static void +xprt_reserve_status(struct rpc_task *task) +{ + struct rpc_xprt *xprt = task->tk_xprt; + struct rpc_rqst *req; + + if (xprt->shutdown) { + task->tk_status = -EIO; + } else if (task->tk_status < 0) { + /* NOP */ + } else if (task->tk_rqstp) { + /* We've already been given a request slot: NOP */ + } else if (!RPCXPRT_CONGESTED(xprt)) { + /* OK: There's room for us. Grab a free slot and bump + * congestion value */ + if (!(req = xprt->free)) { + /* printk("RPC: inconsistent free list!\n"); */ + rpc_debug = ~0; + dprintk("RPC: %4d inconsistent free list " + "(cong %ld cwnd %ld)\n", + task->tk_pid, xprt->cong, xprt->cwnd); + goto bummer; + } + if (req->rq_xid) { + printk("RPC: used rqst slot %p on free list!\n", req); + goto bummer; + } + xprt->free = req->rq_next; + xprt->cong += RPC_CWNDSCALE; + task->tk_rqstp = req; + req->rq_next = NULL; + xprt_request_init(task, xprt); + } else { + task->tk_status = -EAGAIN; + } + + if (xprt->free && !RPCXPRT_CONGESTED(xprt)) + rpc_wake_up_next(&xprt->backlog); + + return; + +bummer: + task->tk_status = -EIO; + xprt->free = NULL; + return; +} + +/* + * Initialize RPC request + */ +static void +xprt_request_init(struct rpc_task *task, struct rpc_xprt *xprt) +{ + struct rpc_rqst *req = task->tk_rqstp; + static u32 xid = 0; + + if (!xid) + xid = jiffies; + + dprintk("RPC: %4d reserved req %p xid %08x\n", task->tk_pid, req, xid); + task->tk_status = 0; + req->rq_gotit = 0; + req->rq_timeout = xprt->timeout; + req->rq_task = task; + req->rq_xprt = xprt; + req->rq_xid = xid++; +} + +/* + * Release an RPC call slot + */ +void +xprt_release(struct rpc_task *task) +{ + struct rpc_xprt *xprt = task->tk_xprt; + struct rpc_rqst *req; + + if (!(req = task->tk_rqstp)) + return; + memset(req, 0, sizeof(*req)); /* mark unused */ + + dprintk("RPC: %4d release request %p\n", task->tk_pid, req); + + /* remove slot from queue of pending */ + disable_bh(NET_BH); + if (task->tk_rpcwait) { + printk("RPC: task of released request still queued!\n"); +#ifdef RPC_DEBUG + printk("RPC: (task is on %s)\n", rpc_qname(task->tk_rpcwait)); +#endif + rpc_del_timer(task); + rpc_remove_wait_queue(task); + } + enable_bh(NET_BH); + + /* Decrease congestion value. If congestion threshold is not yet + * reached, pass on the request slot. + * This looks kind of kludgy, but it guarantees backlogged requests + * are served in order. + */ + xprt->cong -= RPC_CWNDSCALE; + if (!RPCXPRT_CONGESTED(xprt)) { + struct rpc_task *next = rpc_wake_up_next(&xprt->backlog); + + if (next && next->tk_rqstp == 0) { + xprt->cong += RPC_CWNDSCALE; + next->tk_rqstp = req; + xprt_request_init(next, xprt); + return; + } + } + + req->rq_next = xprt->free; + xprt->free = req; +} + +/* + * Set default timeout parameters + */ +void +xprt_default_timeout(struct rpc_timeout *to, int proto) +{ + if (proto == IPPROTO_UDP) + xprt_set_timeout(to, 5, 5 * HZ); + else + xprt_set_timeout(to, 5, 15 * HZ); +} + +/* + * Set constant timeout + */ +void +xprt_set_timeout(struct rpc_timeout *to, unsigned int retr, unsigned long incr) +{ + to->to_current = + to->to_initval = + to->to_increment = incr; + to->to_maxval = incr * retr; + to->to_resrvval = incr * retr; + to->to_retries = retr; + to->to_exponential = 0; +} + +/* + * Initialize an RPC client + */ +static struct rpc_xprt * +xprt_setup(struct socket *sock, int proto, + struct sockaddr_in *ap, struct rpc_timeout *to) +{ + struct rpc_xprt *xprt; + struct rpc_rqst *req; + struct sock *inet; + int i; + + dprintk("RPC: setting up %s transport...\n", + proto == IPPROTO_UDP? "UDP" : "TCP"); + +#if LINUX_VERSION_CODE >= 0x020100 + inet = sock->sk; +#else + inet = (struct sock *) sock->data; +#endif + + if ((xprt = kmalloc(sizeof(struct rpc_xprt), GFP_KERNEL)) == NULL) + return NULL; + memset(xprt, 0, sizeof(*xprt)); /* Nnnngh! */ + + xprt->file = NULL; + xprt->sock = sock; + xprt->inet = inet; + xprt->addr = *ap; + xprt->prot = proto; + xprt->stream = (proto == IPPROTO_TCP)? 1 : 0; + xprt->cwnd = RPC_INITCWND; +#ifdef SOCK_HAS_USER_DATA + inet->user_data = xprt; +#else + xprt->link = sock_list; + sock_list = xprt; +#endif + xprt->old_data_ready = inet->data_ready; + xprt->old_state_change = inet->state_change; + xprt->old_write_space = inet->write_space; + if (proto == IPPROTO_UDP) { + inet->data_ready = udp_data_ready; + } else { + inet->data_ready = tcp_data_ready; + inet->state_change = tcp_state_change; + inet->write_space = tcp_write_space; + xprt->nocong = 1; + } + xprt->connected = 1; + + /* Set timeout parameters */ + if (to) { + xprt->timeout = *to; + xprt->timeout.to_current = to->to_initval; + xprt->timeout.to_resrvval = to->to_maxval << 1; + } else { + xprt_default_timeout(&xprt->timeout, xprt->prot); + } + + xprt->pending = RPC_INIT_WAITQ("xprt_pending"); + xprt->sending = RPC_INIT_WAITQ("xprt_sending"); + xprt->backlog = RPC_INIT_WAITQ("xprt_backlog"); + xprt->reconn = RPC_INIT_WAITQ("xprt_reconn"); + + /* initialize free list */ + for (i = 0, req = xprt->slot; i < RPC_MAXREQS-1; i++, req++) + req->rq_next = req + 1; + req->rq_next = NULL; + xprt->free = xprt->slot; + + dprintk("RPC: created transport %p\n", xprt); + return xprt; +} + +/* + * Create and initialize an RPC client given an open file. + * This is obsolete now. + */ +#if 0 +struct rpc_xprt * +xprt_create(struct file *file, struct sockaddr_in *ap, struct rpc_timeout *to) +{ + struct rpc_xprt *xprt; + struct socket *sock; + int proto; + + if (!file) { + printk("RPC: file == NULL in xprt_create!\n"); + return NULL; + } + + sock = &file->f_inode->u.socket_i; + if (sock->ops->family != AF_INET) { + printk(KERN_WARNING "RPC: only INET sockets supported\n"); + return NULL; + } + + proto = (sock->type == SOCK_DGRAM)? IPPROTO_UDP : IPPROTO_TCP; + if ((xprt = xprt_setup(sock, proto, ap, to)) != NULL) { + xprt->file = file; + file->f_count++; + } + + return xprt; +} +#endif + +/* + * Bind to a reserved port + */ +static inline int +xprt_bindresvport(struct socket *sock) +{ + struct sockaddr_in myaddr; + int err, port; + + memset(&myaddr, 0, sizeof(myaddr)); + myaddr.sin_family = AF_INET; + port = 800; + do { + myaddr.sin_port = htons(port); + err = sock->ops->bind(sock, (struct sockaddr *) &myaddr, + sizeof(myaddr)); + } while (err == -EADDRINUSE && --port > 0); + + if (err < 0) + printk("RPC: Can't bind to reserved port (%d).\n", -err); + + return err; +} + +/* + * Create a client socket given the protocol and peer address. + */ +static struct socket * +xprt_create_socket(int proto, struct sockaddr_in *sap, struct rpc_timeout *to) +{ + struct socket *sock; + int type, err; + + dprintk("RPC: xprt_create_socket(%08lx, %s %d)\n", + sap? ntohl(sap->sin_addr.s_addr) : 0, + (proto == IPPROTO_UDP)? "udp" : "tcp", proto); + + type = (proto == IPPROTO_UDP)? SOCK_DGRAM : SOCK_STREAM; + if ((err = sock_create(AF_INET, type, proto, &sock)) < 0) { + printk("RPC: can't create socket (%d).\n", -err); + goto failed; + } + + /* If the caller has root privs, bind to a reserved port */ + if (!current->fsuid && xprt_bindresvport(sock) < 0) + goto failed; + + if (type == SOCK_STREAM && sap) { + err = sock->ops->connect(sock, (struct sockaddr *) sap, + sizeof(*sap), 0); + if (err < 0) { + printk("RPC: TCP connect failed (%d).\n", -err); + goto failed; + } + } + + return sock; + +failed: + sock_release(sock); + return NULL; +} + +/* + * Create an RPC client transport given the protocol and peer address. + */ +struct rpc_xprt * +xprt_create_proto(int proto, struct sockaddr_in *sap, struct rpc_timeout *to) +{ + struct socket *sock; + struct rpc_xprt *xprt; + + dprintk("RPC: xprt_create_proto called\n"); + + if (!(sock = xprt_create_socket(proto, sap, to))) + return NULL; + + if (!(xprt = xprt_setup(sock, proto, sap, to))) + sock_release(sock); + + return xprt; +} + +/* + * Prepare for transport shutdown. + */ +void +xprt_shutdown(struct rpc_xprt *xprt) +{ + xprt->shutdown = 1; + rpc_wake_up(&xprt->sending); + rpc_wake_up(&xprt->pending); + rpc_wake_up(&xprt->backlog); + rpc_wake_up(&xprt->reconn); +} + +/* + * Destroy an RPC transport, killing off all requests. + */ +int +xprt_destroy(struct rpc_xprt *xprt) +{ +#ifndef SOCK_HAS_USER_DATA + struct rpc_xprt **q; + + for (q = &sock_list; *q && *q != xprt; q = &((*q)->link)) + ; + if (!*q) { + printk(KERN_WARNING "xprt_destroy: unknown socket!\n"); + return -EIO; /* why is there no EBUGGYSOFTWARE */ + } + *q = xprt->link; +#endif + + dprintk("RPC: destroying transport %p\n", xprt); + xprt_close(xprt); + kfree(xprt); + + return 0; +} diff -u --recursive --new-file v2.1.31/linux/net/unix/Makefile linux/net/unix/Makefile --- v2.1.31/linux/net/unix/Makefile Mon Apr 1 21:43:08 1996 +++ linux/net/unix/Makefile Sat Apr 5 12:14:21 1997 @@ -8,7 +8,11 @@ # Note 2! The CFLAGS definition is now in the main makefile... O_TARGET := unix.o -O_OBJS := af_unix.o garbage.o sysctl_net_unix.o +O_OBJS := af_unix.o garbage.o + +ifeq ($(CONFIG_SYSCTL),y) +O_OBJS += sysctl_net_unix.o +endif include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.1.31/linux/net/x25/Makefile linux/net/x25/Makefile --- v2.1.31/linux/net/x25/Makefile Wed Dec 18 02:07:56 1996 +++ linux/net/x25/Makefile Sat Apr 5 12:14:21 1997 @@ -8,8 +8,12 @@ # Note 2! The CFLAGS definition is now in the main makefile... O_TARGET := x25.o -O_OBJS := af_x25.o sysctl_net_x25.o x25_dev.o x25_in.o x25_link.o x25_out.o x25_route.o x25_subr.o x25_timer.o +O_OBJS := af_x25.o x25_dev.o x25_in.o x25_link.o x25_out.o x25_route.o x25_subr.o x25_timer.o M_OBJS := $(O_TARGET) + +ifeq ($(CONFIG_SYSCTL),y) +O_OBJS += sysctl_net_x25.o +endif include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.1.31/linux/net/x25/af_x25.c linux/net/x25/af_x25.c --- v2.1.31/linux/net/x25/af_x25.c Thu Mar 27 14:40:18 1997 +++ linux/net/x25/af_x25.c Sat Apr 5 12:14:21 1997 @@ -1293,7 +1293,9 @@ printk(KERN_INFO "X.25 for Linux. Version 0.1 for Linux 2.1.15\n"); +#ifdef CONFIG_SYSCTL x25_register_sysctl(); +#endif #ifdef CONFIG_PROC_FS proc_net_register(&proc_net_x25); @@ -1337,7 +1339,9 @@ x25_link_free(); x25_route_free(); +#ifdef CONFIG_SYSCTL x25_unregister_sysctl(); +#endif unregister_netdevice_notifier(&x25_dev_notifier);