Merge master.kernel.org:/home/rmk/linux-2.6-arm
authorLinus Torvalds <torvalds@g5.osdl.org>
Wed, 2 Nov 2005 05:32:14 +0000 (21:32 -0800)
committerLinus Torvalds <torvalds@g5.osdl.org>
Wed, 2 Nov 2005 05:32:14 +0000 (21:32 -0800)
271 files changed:
Documentation/filesystems/ntfs.txt
arch/arm/common/locomo.c
arch/arm/common/sa1111.c
arch/arm/common/scoop.c
arch/arm/mach-aaec2000/core.c
arch/arm/mach-h720x/h7202-eval.c
arch/arm/mach-imx/generic.c
arch/arm/mach-imx/mx1ads.c
arch/arm/mach-integrator/integrator_ap.c
arch/arm/mach-integrator/integrator_cp.c
arch/arm/mach-iop3xx/iop321-setup.c
arch/arm/mach-iop3xx/iop331-setup.c
arch/arm/mach-ixp2000/enp2611.c
arch/arm/mach-ixp2000/ixdp2x00.c
arch/arm/mach-ixp2000/ixdp2x01.c
arch/arm/mach-ixp4xx/common.c
arch/arm/mach-lh7a40x/arch-lpd7a40x.c
arch/arm/mach-omap1/board-h2.c
arch/arm/mach-omap1/board-h3.c
arch/arm/mach-omap1/board-innovator.c
arch/arm/mach-omap1/board-netstar.c
arch/arm/mach-omap1/board-osk.c
arch/arm/mach-omap1/board-perseus2.c
arch/arm/mach-omap1/board-voiceblue.c
arch/arm/mach-omap1/devices.c
arch/arm/mach-pxa/corgi.c
arch/arm/mach-pxa/corgi_lcd.c
arch/arm/mach-pxa/corgi_ssp.c
arch/arm/mach-pxa/generic.c
arch/arm/mach-pxa/idp.c
arch/arm/mach-pxa/lubbock.c
arch/arm/mach-pxa/mainstone.c
arch/arm/mach-pxa/poodle.c
arch/arm/mach-pxa/pxa27x.c
arch/arm/mach-pxa/spitz.c
arch/arm/mach-s3c2410/clock.c
arch/arm/mach-s3c2410/cpu.c
arch/arm/mach-s3c2410/devs.c
arch/arm/mach-s3c2410/devs.h
arch/arm/mach-s3c2410/mach-anubis.c
arch/arm/mach-s3c2410/mach-bast.c
arch/arm/mach-s3c2410/mach-h1940.c
arch/arm/mach-s3c2410/mach-n30.c
arch/arm/mach-s3c2410/mach-nexcoder.c
arch/arm/mach-s3c2410/mach-otom.c
arch/arm/mach-s3c2410/mach-rx3715.c
arch/arm/mach-s3c2410/mach-smdk2410.c
arch/arm/mach-s3c2410/mach-smdk2440.c
arch/arm/mach-s3c2410/s3c2410.c
arch/arm/mach-s3c2410/s3c2440.c
arch/arm/mach-sa1100/badge4.c
arch/arm/mach-sa1100/cerf.c
arch/arm/mach-sa1100/collie.c
arch/arm/mach-sa1100/generic.c
arch/arm/mach-sa1100/jornada720.c
arch/arm/mach-sa1100/neponset.c
arch/arm/mach-sa1100/pleb.c
arch/arm/mach-sa1100/simpad.c
arch/arm/mach-versatile/core.c
arch/arm/plat-omap/usb.c
arch/i386/Kconfig
arch/i386/kernel/apic.c
arch/i386/kernel/i8259.c
arch/i386/kernel/io_apic.c
arch/i386/kernel/smpboot.c
arch/i386/kernel/time.c
arch/i386/pci/fixup.c
arch/m32r/kernel/setup_m32700ut.c
arch/m32r/kernel/setup_mappi.c
arch/m32r/kernel/setup_mappi2.c
arch/m32r/kernel/setup_mappi3.c
arch/m32r/kernel/setup_opsput.c
arch/mips/au1000/common/platform.c
arch/ppc/platforms/4xx/ibm440ep.c
arch/ppc/platforms/4xx/ibmstb4.c
arch/ppc/platforms/4xx/redwood5.c
arch/ppc/platforms/4xx/redwood6.c
arch/ppc/platforms/chrp_pegasos_eth.c
arch/ppc/platforms/cpci690.c
arch/ppc/platforms/ev64260.c
arch/ppc/platforms/ev64360.c
arch/ppc/platforms/hdpu.c
arch/ppc/platforms/katana.c
arch/ppc/platforms/radstone_ppc7d.c
arch/ppc/syslib/mpc52xx_devices.c
arch/ppc/syslib/mv64x60.c
arch/ppc/syslib/pq2_devices.c
arch/sh/boards/superh/microdev/setup.c
arch/um/drivers/net_kern.c
arch/um/drivers/ubd_kern.c
arch/xtensa/platform-iss/network.c
drivers/base/platform.c
drivers/block/cfq-iosched.c
drivers/block/floppy.c
drivers/block/noop-iosched.c
drivers/char/s3c2410-rtc.c
drivers/char/sonypi.c
drivers/char/tb0219.c
drivers/char/vr41xx_giu.c
drivers/char/vr41xx_rtc.c
drivers/char/watchdog/mpcore_wdt.c
drivers/char/watchdog/mv64x60_wdt.c
drivers/char/watchdog/pcwd_pci.c
drivers/char/watchdog/s3c2410_wdt.c
drivers/char/watchdog/w83627hf_wdt.c
drivers/eisa/virtual_root.c
drivers/firmware/dcdbas.c
drivers/firmware/dell_rbu.c
drivers/hwmon/hdaps.c
drivers/i2c/busses/i2c-iop3xx.c
drivers/i2c/busses/i2c-isa.c
drivers/i2c/busses/i2c-ixp2000.c
drivers/i2c/busses/i2c-ixp4xx.c
drivers/i2c/busses/i2c-mpc.c
drivers/i2c/busses/i2c-mv64xxx.c
drivers/i2c/busses/i2c-pxa.c
drivers/i2c/busses/i2c-s3c2410.c
drivers/i2c/chips/isp1301_omap.c
drivers/i2c/i2c-core.c
drivers/i2c/i2c-dev.c
drivers/input/evdev.c
drivers/input/input.c
drivers/input/keyboard/Kconfig
drivers/input/keyboard/corgikbd.c
drivers/input/keyboard/lkkbd.c
drivers/input/keyboard/spitzkbd.c
drivers/input/misc/pcspkr.c
drivers/input/mouse/Kconfig
drivers/input/serio/ct82c710.c
drivers/input/serio/i8042.c
drivers/input/serio/maceps2.c
drivers/input/serio/q40kbd.c
drivers/input/serio/rpckbd.c
drivers/input/touchscreen/corgi_ts.c
drivers/macintosh/adbhid.c
drivers/mfd/mcp-sa11x0.c
drivers/misc/hdpuftrs/hdpu_cpustate.c
drivers/misc/hdpuftrs/hdpu_nexus.c
drivers/mmc/pxamci.c
drivers/mmc/wbsd.c
drivers/mtd/maps/bast-flash.c
drivers/mtd/maps/integrator-flash.c
drivers/mtd/maps/ixp2000.c
drivers/mtd/maps/ixp4xx.c
drivers/mtd/maps/omap_nor.c
drivers/mtd/maps/plat-ram.c
drivers/mtd/maps/sa1100-flash.c
drivers/mtd/nand/s3c2410.c
drivers/net/depca.c
drivers/net/dm9000.c
drivers/net/gianfar.c
drivers/net/gianfar_mii.c
drivers/net/irda/pxaficp_ir.c
drivers/net/irda/sa1100_ir.c
drivers/net/irda/smsc-ircc2.c
drivers/net/jazzsonic.c
drivers/net/macsonic.c
drivers/net/mipsnet.c
drivers/net/mv643xx_eth.c
drivers/net/smc91x.c
drivers/net/tokenring/proteon.c
drivers/net/tokenring/skisa.c
drivers/pcmcia/au1000_generic.c
drivers/pcmcia/hd64465_ss.c
drivers/pcmcia/i82365.c
drivers/pcmcia/m32r_cfc.c
drivers/pcmcia/m32r_pcc.c
drivers/pcmcia/omap_cf.c
drivers/pcmcia/pxa2xx_base.c
drivers/pcmcia/pxa2xx_mainstone.c
drivers/pcmcia/pxa2xx_sharpsl.c
drivers/pcmcia/sa1100_generic.c
drivers/pcmcia/tcic.c
drivers/pcmcia/vrc4171_card.c
drivers/scsi/hosts.c
drivers/scsi/ide-scsi.c
drivers/scsi/libata-core.c
drivers/scsi/libata-scsi.c
drivers/scsi/libata.h
drivers/serial/8250.c
drivers/serial/imx.c
drivers/serial/mpc52xx_uart.c
drivers/serial/mpsc.c
drivers/serial/pxa.c
drivers/serial/s3c2410.c
drivers/serial/sa1100.c
drivers/serial/vr41xx_siu.c
drivers/usb/gadget/dummy_hcd.c
drivers/usb/gadget/lh7a40x_udc.c
drivers/usb/gadget/omap_udc.c
drivers/usb/gadget/pxa2xx_udc.c
drivers/usb/host/isp116x-hcd.c
drivers/usb/host/ohci-au1xxx.c
drivers/usb/host/ohci-lh7a404.c
drivers/usb/host/ohci-omap.c
drivers/usb/host/ohci-ppc-soc.c
drivers/usb/host/ohci-pxa27x.c
drivers/usb/host/ohci-s3c2410.c
drivers/usb/host/pci-quirks.c
drivers/usb/host/sl811-hcd.c
drivers/usb/host/sl811_cs.c
drivers/video/acornfb.c
drivers/video/arcfb.c
drivers/video/backlight/corgi_bl.c
drivers/video/dnfb.c
drivers/video/epson1355fb.c
drivers/video/gbefb.c
drivers/video/imxfb.c
drivers/video/pxafb.c
drivers/video/q40fb.c
drivers/video/s1d13xxxfb.c
drivers/video/s3c2410fb.c
drivers/video/sa1100fb.c
drivers/video/sgivwfb.c
drivers/video/vesafb.c
drivers/video/vfb.c
drivers/video/w100fb.c
fs/cifs/AUTHORS
fs/cifs/CHANGES
fs/cifs/README
fs/cifs/TODO
fs/cifs/asn1.c
fs/cifs/cifs_debug.c
fs/cifs/cifs_debug.h
fs/cifs/cifs_fs_sb.h
fs/cifs/cifsfs.c
fs/cifs/cifsfs.h
fs/cifs/cifsglob.h
fs/cifs/cifspdu.h
fs/cifs/cifsproto.h
fs/cifs/cifssmb.c
fs/cifs/connect.c
fs/cifs/dir.c
fs/cifs/fcntl.c
fs/cifs/file.c
fs/cifs/inode.c
fs/cifs/link.c
fs/cifs/misc.c
fs/cifs/netmisc.c
fs/cifs/ntlmssp.h
fs/cifs/readdir.c
fs/cifs/rfc1002pdu.h
fs/cifs/transport.c
fs/fs-writeback.c
fs/ntfs/ChangeLog
fs/ntfs/Makefile
fs/ntfs/aops.c
fs/ntfs/attrib.c
fs/ntfs/attrib.h
fs/ntfs/file.c
fs/ntfs/inode.c
fs/ntfs/layout.h
fs/ntfs/lcnalloc.c
fs/ntfs/lcnalloc.h
fs/ntfs/malloc.h
fs/ntfs/mft.c
fs/ntfs/super.c
include/asm-i386/apic.h
include/asm-i386/hw_irq.h
include/asm-i386/mach-default/smpboot_hooks.h
include/asm-i386/mach-visws/smpboot_hooks.h
include/asm-ppc/ppc_sys.h
include/linux/device.h
include/linux/platform_device.h [new file with mode: 0644]
include/linux/serial_8250.h
include/sound/emu10k1.h
init/main.c
mm/swap.c
sound/arm/pxa2xx-ac97.c
sound/core/init.c
sound/pci/emu10k1/emu10k1_main.c

index a5fbc8e897fa6f2f4a768241c66372de7b3366e7..614de31249019d6ab29d0f59a0fccb90c971e5e1 100644 (file)
@@ -50,9 +50,14 @@ userspace utilities, etc.
 Features
 ========
 
-- This is a complete rewrite of the NTFS driver that used to be in the kernel.
-  This new driver implements NTFS read support and is functionally equivalent
-  to the old ntfs driver.
+- This is a complete rewrite of the NTFS driver that used to be in the 2.4 and
+  earlier kernels.  This new driver implements NTFS read support and is
+  functionally equivalent to the old ntfs driver and it also implements limited
+  write support.  The biggest limitation at present is that files/directories
+  cannot be created or deleted.  See below for the list of write features that
+  are so far supported.  Another limitation is that writing to compressed files
+  is not implemented at all.  Also, neither read nor write access to encrypted
+  files is so far implemented.
 - The new driver has full support for sparse files on NTFS 3.x volumes which
   the old driver isn't happy with.
 - The new driver supports execution of binaries due to mmap() now being
@@ -78,7 +83,20 @@ Features
 - The new driver supports fsync(2), fdatasync(2), and msync(2).
 - The new driver supports readv(2) and writev(2).
 - The new driver supports access time updates (including mtime and ctime).
-
+- The new driver supports truncate(2) and open(2) with O_TRUNC.  But at present
+  only very limited support for highly fragmented files, i.e. ones which have
+  their data attribute split across multiple extents, is included.  Another
+  limitation is that at present truncate(2) will never create sparse files,
+  since to mark a file sparse we need to modify the directory entry for the
+  file and we do not implement directory modifications yet.
+- The new driver supports write(2) which can both overwrite existing data and
+  extend the file size so that you can write beyond the existing data.  Also,
+  writing into sparse regions is supported and the holes are filled in with
+  clusters.  But at present only limited support for highly fragmented files,
+  i.e. ones which have their data attribute split across multiple extents, is
+  included.  Another limitation is that write(2) will never create sparse
+  files, since to mark a file sparse we need to modify the directory entry for
+  the file and we do not implement directory modifications yet.
 
 Supported mount options
 =======================
@@ -439,6 +457,22 @@ ChangeLog
 
 Note, a technical ChangeLog aimed at kernel hackers is in fs/ntfs/ChangeLog.
 
+2.1.25:
+       - Write support is now extended with write(2) being able to both
+         overwrite existing file data and to extend files.  Also, if a write
+         to a sparse region occurs, write(2) will fill in the hole.  Note,
+         mmap(2) based writes still do not support writing into holes or
+         writing beyond the initialized size.
+       - Write support has a new feature and that is that truncate(2) and
+         open(2) with O_TRUNC are now implemented thus files can be both made
+         smaller and larger.
+       - Note: Both write(2) and truncate(2)/open(2) with O_TRUNC still have
+         limitations in that they
+         - only provide limited support for highly fragmented files.
+         - only work on regular, i.e. uncompressed and unencrypted files.
+         - never create sparse files although this will change once directory
+           operations are implemented.
+       - Lots of bug fixes and enhancements across the board.
 2.1.24:
        - Support journals ($LogFile) which have been modified by chkdsk.  This
          means users can boot into Windows after we marked the volume dirty.
index 5cdb4122f057ca64874024d50b1750f9375cb649..ad55680726ed5b10fac7efb9852d9294d940cf26 100644 (file)
@@ -22,7 +22,7 @@
 #include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/ioport.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 
index 21e2a518ad3ab71c85edf68e47468e04666d1866..174aa86ee816b49b2e41dcd2e2cfca64b2c3abbf 100644 (file)
@@ -22,7 +22,7 @@
 #include <linux/ptrace.h>
 #include <linux/errno.h>
 #include <linux/ioport.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/dma-mapping.h>
index 4af0cf5f3bfbc8d4f8fce84fc508f465c6ddf10a..bb4eff61441307458f92a104b0fa655542e118b7 100644 (file)
@@ -13,8 +13,7 @@
 
 #include <linux/device.h>
 #include <linux/string.h>
-#include <linux/slab.h>
-
+#include <linux/platform_device.h>
 #include <asm/io.h>
 #include <asm/hardware/scoop.h>
 
index 0c53dab8090593028381a6c23cdf265779745eb6..4e706d9ad368ee63950a5cea90684eb50b272a26 100644 (file)
@@ -13,7 +13,7 @@
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/list.h>
 #include <linux/errno.h>
 #include <linux/dma-mapping.h>
index db9078ad008c550a0b5c924684df12f280a359e2..d75c8221d2a595d8fddfbf790826be7226797b45 100644 (file)
@@ -18,7 +18,7 @@
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/string.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/setup.h>
 #include <asm/types.h>
index 837d7f0bda4c8f9b610064551414e66f1cf5a213..37613ad68366eda2615d384501dbcfc4dbb6ffa7 100644 (file)
@@ -22,7 +22,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  */
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
index 4cbdc1fe04b1cff7e0f9315b1367f6a0d24b396d..708e1b3faa1466cddb97867403510c8d301824a3 100644 (file)
@@ -14,6 +14,7 @@
 
 #include <linux/device.h>
 #include <linux/init.h>
+#include <linux/platform_device.h>
 #include <asm/system.h>
 #include <asm/hardware.h>
 #include <asm/irq.h>
index 764ceb49470a88975044d383cfe90e083fd77c70..4c0f7c65facf93f215e510d628b9c17d9cdbcb00 100644 (file)
@@ -21,7 +21,7 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/list.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/sysdev.h>
index aa34c58b96c411abda5198ffaa7d44ec53f290d2..93f7ccb22c27f4e12ad4dc9ba180baa3ac177aed 100644 (file)
@@ -11,7 +11,7 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/list.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 #include <linux/slab.h>
 #include <linux/string.h>
index bb5091223b638120291b9ee15e78693ff227638f..80770233b8d40d7970eee8eaf54ed9df2e715da4 100644 (file)
@@ -16,7 +16,7 @@
 #include <linux/init.h>
 #include <linux/major.h>
 #include <linux/fs.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/serial.h>
 #include <linux/tty.h>
 #include <linux/serial_core.h>
index a2533c3ab42f6671f26d5d84250b30410e5ac1f7..53f60614498be508777a95ad4fd87f1532b74001 100644 (file)
@@ -15,7 +15,7 @@
 #include <linux/init.h>
 #include <linux/major.h>
 #include <linux/fs.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/serial.h>
 #include <linux/tty.h>
 #include <linux/serial_core.h>
index b91fe7c80fac692a1ad0fff9855404f387796afe..7719c478aa84b6d44ac9230ac96d1ea71a7400bf 100644 (file)
@@ -32,7 +32,7 @@
 #include <linux/serial.h>
 #include <linux/tty.h>
 #include <linux/serial_core.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
index 00af934dea3957d49ef903d8121463e6261e0c77..d628da56b4bcf6d4ee5db0cfdc236f137880e8d4 100644 (file)
@@ -20,7 +20,7 @@
 #include <linux/mm.h>
 #include <linux/sched.h>
 #include <linux/interrupt.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/bitops.h>
 #include <linux/pci.h>
 #include <linux/ioport.h>
index a4e507c8a1d94a35a567b168c66173481504f762..e6a882f35da2bf8a43397716a7ca9a2b65077eed 100644 (file)
@@ -29,7 +29,7 @@
 #include <linux/serial.h>
 #include <linux/tty.h>
 #include <linux/serial_core.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
index 6c396447c4e078d8ce7e1807c072edfc8c704bb7..f3c687cf00713dcbcaf20eb4689aac5f05818b98 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/serial.h>
 #include <linux/sched.h>
 #include <linux/tty.h>
+#include <linux/platform_device.h>
 #include <linux/serial_core.h>
 #include <linux/bootmem.h>
 #include <linux/interrupt.h>
index a20eabc132b08464c43d65d8915051316cf91b0a..4eb962fdb3a8554530887966f80d7b71ace4eeb3 100644 (file)
@@ -10,7 +10,7 @@
 
 #include <linux/tty.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/interrupt.h>
 
 #include <asm/hardware.h>
index d46a70063b0c61821088ae2c8f483c6a9356454f..4ee6bd8a50b8b105699e4c5698883bcd4526647f 100644 (file)
@@ -21,7 +21,7 @@
 
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/delay.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
index 2798613696fa7e0781609bb61132858d25089e27..fc824361430d86951f184e8b23f82cee8ee07928 100644 (file)
@@ -19,7 +19,7 @@
 #include <linux/init.h>
 #include <linux/major.h>
 #include <linux/kernel.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/errno.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
index fd9183ff2ed53c03252acbb5faa86f687c567ed4..a2eac853b2da863fe0f0a59c4bfc90dafa6c092f 100644 (file)
@@ -18,7 +18,7 @@
 
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/delay.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
index d904e643f5ec2c30fabf5d8438f5e40cdec82ad1..c851c2e4dfcb74cc97f988feb93308f53d159500 100644 (file)
@@ -11,7 +11,7 @@
  */
 
 #include <linux/delay.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
index 21103df50415a7007428f04224b54c8c2127b023..a88524e7c315ba1f95a423f5408d5250214cf540 100644 (file)
@@ -28,7 +28,7 @@
 
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/interrupt.h>
 
 #include <linux/mtd/mtd.h>
index 2ba26e239108f87e9b51f38a6ade736256ffd366..354b157acb3a2071193c450630e846618db69020 100644 (file)
@@ -13,7 +13,7 @@
 
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/delay.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
index bf30b1acda0b97a1c9795a0006622c7db6935265..3f018b296861e4def60cd01df13fb470a4fefd54 100644 (file)
@@ -13,7 +13,7 @@
  */
 
 #include <linux/delay.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
index e8b3981444cd0d9da280a58374efa28bd9154bdb..3c5d901efeaa88d94b17be82c029c3ba082dab31 100644 (file)
@@ -13,7 +13,7 @@
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/hardware.h>
 #include <asm/io.h>
index 656f73bbcb5a9d6c80fb130b95c1cf6e954764ce..eb5f6d744a4a77778d290ba063b18cccd01973d5 100644 (file)
@@ -14,7 +14,7 @@
 
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/major.h>
 #include <linux/fs.h>
 #include <linux/interrupt.h>
index 370df113dc066433640874481e7d055a931a7f39..54162ba954142d615bb47832128e288c90ba023f 100644 (file)
@@ -17,7 +17,7 @@
 
 #include <linux/delay.h>
 #include <linux/kernel.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/module.h>
 #include <asm/arch/akita.h>
 #include <asm/arch/corgi.h>
index 136c269db0b7965c8b2a93e9c4abc326ab56714d..591e5f32dbeccdb726ab491b31d080f60376b54b 100644 (file)
@@ -15,7 +15,7 @@
 #include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <asm/hardware.h>
 #include <asm/mach-types.h>
 
index 9c0289333301e9c5f0c0dc252c9990fbd20a8646..9b48a90aefce9d4a25910f513d190c1e7ae7d8c5 100644 (file)
@@ -20,7 +20,7 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/delay.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/ioport.h>
 #include <linux/pm.h>
 #include <linux/string.h>
index 01a83ab09ac340a8f7fadb0792a948c067c93e08..7de159e2ab42e173b90d3a84d8b2e0b894f812fa 100644 (file)
@@ -18,7 +18,7 @@
 
 #include <linux/init.h>
 #include <linux/interrupt.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/fb.h>
 
 #include <asm/setup.h>
index 689f31349bd62df5a3a33a6e694a45cf14150073..9c6e77faec5b68ed7211ba430733eeb1cfa307e7 100644 (file)
@@ -14,7 +14,7 @@
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/sysdev.h>
 #include <linux/major.h>
 #include <linux/fb.h>
index a48c64026e1fbb4be74321b305874b20ad7828b0..887a8cb7b7219097464a44ab477ff0ae98bce320 100644 (file)
@@ -14,7 +14,7 @@
  */
 
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/sysdev.h>
 #include <linux/interrupt.h>
 #include <linux/sched.h>
index 6d413f6701a77ebd8afdeba39ec4a09fd1118081..ad6a13f95a62cfcae51bc94ffc2fd1f243f17be7 100644 (file)
@@ -16,7 +16,7 @@
  */
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/fb.h>
 
 #include <asm/hardware.h>
index 09a5d593f04b27c2558850e76c0e99dcee861967..c722a9a91fcce243b9fce9a77f2c15f3e50b939a 100644 (file)
@@ -16,7 +16,7 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/pm.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/hardware.h>
 #include <asm/irq.h>
index b838842b6a2050438cc351698cbf63a54ae68778..6c6878cd2207db94f3b70343beef634fbb6491f2 100644 (file)
@@ -14,7 +14,7 @@
 
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/delay.h>
 #include <linux/major.h>
 #include <linux/fs.h>
index 8b3d5dc35de58866dd7ddda31235ac1929c09437..82e8253b1fa04d556c4da51b2db7ca905bb642c3 100644 (file)
@@ -32,7 +32,7 @@
 #include <linux/list.h>
 #include <linux/errno.h>
 #include <linux/err.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/sysdev.h>
 
 #include <linux/interrupt.h>
index ca366e9e264da5c908b072e49fa3fdd43a995cdd..687fe371369d5e42d01fe5fb988b6f536e86b0f5 100644 (file)
@@ -26,7 +26,7 @@
 #include <linux/module.h>
 #include <linux/interrupt.h>
 #include <linux/ioport.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/hardware.h>
 #include <asm/irq.h>
index 08bc7d95a45d78c0290b8d5c15eef4c934fd8105..f58406e6ef5a69a3d65103597813ecffdd3a587d 100644 (file)
@@ -24,7 +24,7 @@
 #include <linux/list.h>
 #include <linux/timer.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
index d6328f96728bfb68d72ff068300f1fe3f9638fb8..52c4bab5c7615590ec167bfe0658ba2fe1a2e663 100644 (file)
@@ -15,6 +15,7 @@
  *     10-Feb-2005 BJD  Added camera from guillaume.gourat@nexvision.tv
 */
 #include <linux/config.h>
+#include <linux/platform_device.h>
 
 extern struct platform_device *s3c24xx_uart_devs[];
 
index 5ae80f4e3e672ca3dcf187aa4e96fdc1f27a1752..8390b685c2b61853ac7ead39016427e660a79447 100644 (file)
@@ -21,7 +21,7 @@
 #include <linux/list.h>
 #include <linux/timer.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
index c1b5c63ec24a7b31bf7a838158a2e3cb6964a10b..0b71c896bbd1ac5fe5ebb9ae55487cf01992e9d4 100644 (file)
@@ -41,7 +41,7 @@
 #include <linux/list.h>
 #include <linux/timer.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/dm9000.h>
 
 #include <asm/mach/arch.h>
index 7efeaaad2361e71f2b75d4b2beb29ae493a329b1..0aa8760598f74bf0d27404005623a938693dc19d 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/list.h>
 #include <linux/timer.h>
 #include <linux/init.h>
+#include <linux/platform_device.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
index 5c0f2b091f95146d850d2ee4197f9b5b6d4dcbf1..378d640ab00bd0029f3e5cb89831319d43eddbfe 100644 (file)
@@ -20,7 +20,7 @@
 #include <linux/timer.h>
 #include <linux/init.h>
 #include <linux/delay.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/kthread.h>
 
 #include <asm/mach/arch.h>
index c22f8216032d8d8a5e88c5d7e01bfc51bb580253..42b0eeff2e0f1da322805b24d0941f75f357fa33 100644 (file)
@@ -19,7 +19,7 @@
 #include <linux/timer.h>
 #include <linux/init.h>
 #include <linux/string.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <linux/mtd/map.h>
 
index ad1459e402e286671dc3ded975a3416b24c6a69e..a2eb9ed48fcdc1e53541a1134c6dd703d3d4361d 100644 (file)
@@ -15,7 +15,7 @@
 #include <linux/list.h>
 #include <linux/timer.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
index 7ff8e4d574c05a5bf34482bb003412309e0e1828..24d69019a843d3401d1adb9ec90836a3f4965bdf 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/init.h>
 #include <linux/tty.h>
 #include <linux/console.h>
+#include <linux/platform_device.h>
 #include <linux/serial_core.h>
 #include <linux/serial.h>
 
index 2eda55a6b678bddd2b5fdb6f7c7f17db9bebeb86..2c91965ee1c89292e104703c6111029234449d2d 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/list.h>
 #include <linux/timer.h>
 #include <linux/init.h>
+#include <linux/platform_device.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
index 6950e61b79149022c368beac1a6d2b910c812f9c..d666c621ad064a3811885a792171c1ad82a3e762 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/list.h>
 #include <linux/timer.h>
 #include <linux/init.h>
+#include <linux/platform_device.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
index a8bf5ec826028c44db4b0ef22b81dc11421affdd..0a2013a7654922161631ae5da0043834f9ed7e7e 100644 (file)
@@ -27,7 +27,7 @@
 #include <linux/list.h>
 #include <linux/timer.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
index 833fa36bce057764843e3be433d406fd1ec2f3ed..4d63e7133b48438014250da15e99b79f71637afb 100644 (file)
@@ -26,7 +26,7 @@
 #include <linux/list.h>
 #include <linux/timer.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/sysdev.h>
 
 #include <asm/mach/arch.h>
index c92cebff7f8e758b653b053c612b82cb8041ba92..edccd5eb06be917b257d0ec38f1e135734be7243 100644 (file)
@@ -16,7 +16,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/delay.h>
 #include <linux/tty.h>
 #include <linux/mtd/mtd.h>
index 23cb748852751db95f0437e97c4052f2dd1865c4..508593722bc741527ffdc276342550ad7c93f860 100644 (file)
@@ -14,7 +14,7 @@
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/tty.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 
index 7fd6e29c36b79d6fb1cc7091695a6c5a4b56771a..522abc036d3a35521bc9acfe4721c5e2cbd025ed 100644 (file)
@@ -21,7 +21,7 @@
 #include <linux/kernel.h>
 #include <linux/tty.h>
 #include <linux/delay.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 #include <linux/timer.h>
index 83eba8b54816681f217807dd2ef0dccd76d10288..2abdc419e9848a8056918d62cdb72a1841bdb806 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/cpufreq.h>
 #include <linux/ioport.h>
 #include <linux/sched.h>       /* just for sched_clock() - funny that */
+#include <linux/platform_device.h>
 
 #include <asm/div64.h>
 #include <asm/hardware.h>
index 89af0c831e8fde6621856dec34c23dc0dfef774b..2f671cc3cb99f793a1a186ccae7eeffebd699883 100644 (file)
@@ -6,7 +6,7 @@
 #include <linux/kernel.h>
 #include <linux/tty.h>
 #include <linux/delay.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/ioport.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
index 052e4caedb89322e510d0eba42bc742a9c3850bd..69f1970646c628e3a9e8f68a2adddf511a16297b 100644 (file)
@@ -8,7 +8,7 @@
 #include <linux/tty.h>
 #include <linux/ioport.h>
 #include <linux/serial_core.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/slab.h>
 
 #include <asm/hardware.h>
index e17b58fb9c9c310aa3cf91b3bb783ecd55e52b1c..58c18f9e9b7bf2a207d2ff03be48820609683980 100644 (file)
@@ -6,7 +6,7 @@
 #include <linux/kernel.h>
 #include <linux/tty.h>
 #include <linux/ioport.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <linux/mtd/partitions.h>
 
index cfb6658e5cdf5fac50e4d9256947d47268483f02..439ddc9b06d6f1dda4159ab73579410df6340bab 100644 (file)
@@ -10,7 +10,7 @@
 #include <linux/proc_fs.h>
 #include <linux/string.h> 
 #include <linux/pm.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 
index 7e4bdd07f4afb4c82778a4c3c796d39684587103..a1ca46630dda469140f5ef2b2d33919bc6c9551a 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/init.h>
 #include <linux/device.h>
 #include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
 #include <linux/sysdev.h>
 #include <linux/interrupt.h>
 
index 14a836d7ac250a4ebee43b3cca8edf1b5e70cba8..205e2d0b826d4f4ff53f24c73a71970b88be10a2 100644 (file)
@@ -26,7 +26,7 @@
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/usb_otg.h>
 
 #include <asm/io.h>
index 5383e5e2d9b7e564f675516f69b477b280989e02..bac0da731ee3ae98065d5771148648ad1df53940 100644 (file)
@@ -1042,8 +1042,3 @@ config X86_TRAMPOLINE
        bool
        depends on X86_SMP || (X86_VOYAGER && SMP)
        default y
-
-config PC
-       bool
-       depends on X86 && !EMBEDDED
-       default y
index 9204be6eedb3c5e4d25c1add893359de85fda1ab..7c724ffa08bb9617e4a1834fb20e5924565e281f 100644 (file)
@@ -803,7 +803,6 @@ no_apic:
 
 void __init init_apic_mappings(void)
 {
-       unsigned int orig_apicid;
        unsigned long apic_phys;
 
        /*
@@ -825,11 +824,8 @@ void __init init_apic_mappings(void)
         * Fetch the APIC ID of the BSP in case we have a
         * default configuration (or the MP table is broken).
         */
-       orig_apicid = boot_cpu_physical_apicid;
-       boot_cpu_physical_apicid = GET_APIC_ID(apic_read(APIC_ID));
-       if ((orig_apicid != -1U) && (orig_apicid != boot_cpu_physical_apicid))
-               printk(KERN_WARNING "Boot APIC ID in local APIC unexpected (%d vs %d)",
-                       orig_apicid, boot_cpu_physical_apicid);
+       if (boot_cpu_physical_apicid == -1U)
+               boot_cpu_physical_apicid = GET_APIC_ID(apic_read(APIC_ID));
 
 #ifdef CONFIG_X86_IO_APIC
        {
@@ -1259,81 +1255,40 @@ fastcall void smp_error_interrupt(struct pt_regs *regs)
 }
 
 /*
- * This initializes the IO-APIC and APIC hardware.
+ * This initializes the IO-APIC and APIC hardware if this is
+ * a UP kernel.
  */
-int __init APIC_init(void)
+int __init APIC_init_uniprocessor (void)
 {
-       if (enable_local_apic < 0) {
-               printk(KERN_INFO "APIC disabled\n");
-               return -1;
-       }
+       if (enable_local_apic < 0)
+               clear_bit(X86_FEATURE_APIC, boot_cpu_data.x86_capability);
 
-       /* See if we have a SMP configuration or have forced enabled
-        * the local apic.
-        */
-       if (!smp_found_config && !acpi_lapic && !cpu_has_apic) {
-               enable_local_apic = -1;
+       if (!smp_found_config && !cpu_has_apic)
                return -1;
-       }
 
        /*
-        * Complain if the BIOS pretends there is an apic.
-        * Then get out because we don't have an a local apic.
+        * Complain if the BIOS pretends there is one.
         */
        if (!cpu_has_apic && APIC_INTEGRATED(apic_version[boot_cpu_physical_apicid])) {
                printk(KERN_ERR "BIOS bug, local APIC #%d not detected!...\n",
                        boot_cpu_physical_apicid);
-               printk(KERN_ERR "... forcing use of dummy APIC emulation. (tell your hw vendor)\n");
-               enable_local_apic = -1;
                return -1;
        }
 
        verify_local_APIC();
 
-       /*
-        * Should not be necessary because the MP table should list the boot
-        * CPU too, but we do it for the sake of robustness anyway.
-        * Makes no sense to do this check in clustered apic mode, so skip it
-        */
-       if (!check_phys_apicid_present(boot_cpu_physical_apicid)) {
-               printk("weird, boot CPU (#%d) not listed by the BIOS.\n",
-                               boot_cpu_physical_apicid);
-               physid_set(boot_cpu_physical_apicid, phys_cpu_present_map);
-       }
-
-       /*
-        * Switch from PIC to APIC mode.
-        */
        connect_bsp_APIC();
-       setup_local_APIC();
 
-#ifdef CONFIG_X86_IO_APIC
-       /*
-        * Now start the IO-APICs
-        */
-       if (smp_found_config && !skip_ioapic_setup && nr_ioapics)
-               setup_IO_APIC();
-#endif
-       return 0;
-}
+       phys_cpu_present_map = physid_mask_of_physid(boot_cpu_physical_apicid);
 
-void __init APIC_late_time_init(void)
-{
-       /* Improve our loops per jiffy estimate */
-       loops_per_jiffy = ((1000 + HZ - 1)/HZ)*cpu_khz;
-       boot_cpu_data.loops_per_jiffy = loops_per_jiffy;
-       cpu_data[0].loops_per_jiffy = loops_per_jiffy;
-
-       /* setup_apic_nmi_watchdog doesn't work properly before cpu_khz is
-        * initialized.  So redo it here to ensure the boot cpu is setup
-        * properly.
-        */
-       if (nmi_watchdog == NMI_LOCAL_APIC)
-               setup_apic_nmi_watchdog();
+       setup_local_APIC();
 
 #ifdef CONFIG_X86_IO_APIC
-       if (smp_found_config && !skip_ioapic_setup && nr_ioapics)
-               IO_APIC_late_time_init();
+       if (smp_found_config)
+               if (!skip_ioapic_setup && nr_ioapics)
+                       setup_IO_APIC();
 #endif
        setup_boot_APIC_clock();
+
+       return 0;
 }
index d86f2490928449ce15acfc8ffff5522e11139453..323ef8ab3244e880c1b32468a12122990ba0ceba 100644 (file)
@@ -435,8 +435,4 @@ void __init init_IRQ(void)
                setup_irq(FPU_IRQ, &fpu_irq);
 
        irq_ctx_init(smp_processor_id());
-
-#ifdef CONFIG_X86_LOCAL_APIC
-       APIC_init();
-#endif
 }
index 5a77c52b20a934d2d9a168ee20a1a965201e1818..cc5d7ac5b2e7dfa5a157bb0dffe57971bc037ed4 100644 (file)
@@ -2387,15 +2387,11 @@ void __init setup_IO_APIC(void)
        sync_Arb_IDs();
        setup_IO_APIC_irqs();
        init_IO_APIC_traps();
+       check_timer();
        if (!acpi_ioapic)
                print_IO_APIC();
 }
 
-void __init IO_APIC_late_time_init(void)
-{
-       check_timer();
-}
-
 /*
  *     Called after all the initialization is done. If we didnt find any
  *     APIC bugs then we can allow the modify fast path
index 5a2bbe0c4ffff7ffcc5344d3d7015bd6651061d8..01b618e73ecd14ba6f9dedea08a718865715d266 100644 (file)
@@ -1078,16 +1078,6 @@ void *xquad_portio;
 EXPORT_SYMBOL(xquad_portio);
 #endif
 
-/*
- * Fall back to non SMP mode after errors.
- *
- */
-static __init void disable_smp(void)
-{
-       cpu_set(0, cpu_sibling_map[0]);
-       cpu_set(0, cpu_core_map[0]);
-}
-
 static void __init smp_boot_cpus(unsigned int max_cpus)
 {
        int apicid, cpu, bit, kicked;
@@ -1100,6 +1090,7 @@ static void __init smp_boot_cpus(unsigned int max_cpus)
        printk("CPU%d: ", 0);
        print_cpu_info(&cpu_data[0]);
 
+       boot_cpu_physical_apicid = GET_APIC_ID(apic_read(APIC_ID));
        boot_cpu_logical_apicid = logical_smp_processor_id();
        x86_cpu_to_apicid[0] = boot_cpu_physical_apicid;
 
@@ -1111,27 +1102,68 @@ static void __init smp_boot_cpus(unsigned int max_cpus)
        cpus_clear(cpu_core_map[0]);
        cpu_set(0, cpu_core_map[0]);
 
-       map_cpu_to_logical_apicid();
-
        /*
         * If we couldn't find an SMP configuration at boot time,
         * get out of here now!
         */
        if (!smp_found_config && !acpi_lapic) {
                printk(KERN_NOTICE "SMP motherboard not detected.\n");
-               disable_smp();
+               smpboot_clear_io_apic_irqs();
+               phys_cpu_present_map = physid_mask_of_physid(0);
+               if (APIC_init_uniprocessor())
+                       printk(KERN_NOTICE "Local APIC not detected."
+                                          " Using dummy APIC emulation.\n");
+               map_cpu_to_logical_apicid();
+               cpu_set(0, cpu_sibling_map[0]);
+               cpu_set(0, cpu_core_map[0]);
+               return;
+       }
+
+       /*
+        * Should not be necessary because the MP table should list the boot
+        * CPU too, but we do it for the sake of robustness anyway.
+        * Makes no sense to do this check in clustered apic mode, so skip it
+        */
+       if (!check_phys_apicid_present(boot_cpu_physical_apicid)) {
+               printk("weird, boot CPU (#%d) not listed by the BIOS.\n",
+                               boot_cpu_physical_apicid);
+               physid_set(hard_smp_processor_id(), phys_cpu_present_map);
+       }
+
+       /*
+        * If we couldn't find a local APIC, then get out of here now!
+        */
+       if (APIC_INTEGRATED(apic_version[boot_cpu_physical_apicid]) && !cpu_has_apic) {
+               printk(KERN_ERR "BIOS bug, local APIC #%d not detected!...\n",
+                       boot_cpu_physical_apicid);
+               printk(KERN_ERR "... forcing use of dummy APIC emulation. (tell your hw vendor)\n");
+               smpboot_clear_io_apic_irqs();
+               phys_cpu_present_map = physid_mask_of_physid(0);
+               cpu_set(0, cpu_sibling_map[0]);
+               cpu_set(0, cpu_core_map[0]);
                return;
        }
 
+       verify_local_APIC();
+
        /*
         * If SMP should be disabled, then really disable it!
         */
-       if (!max_cpus || (enable_local_apic < 0)) {
-               printk(KERN_INFO "SMP mode deactivated.\n");
-               disable_smp();
+       if (!max_cpus) {
+               smp_found_config = 0;
+               printk(KERN_INFO "SMP mode deactivated, forcing use of dummy APIC emulation.\n");
+               smpboot_clear_io_apic_irqs();
+               phys_cpu_present_map = physid_mask_of_physid(0);
+               cpu_set(0, cpu_sibling_map[0]);
+               cpu_set(0, cpu_core_map[0]);
                return;
        }
 
+       connect_bsp_APIC();
+       setup_local_APIC();
+       map_cpu_to_logical_apicid();
+
+
        setup_portio_remap();
 
        /*
@@ -1212,6 +1244,10 @@ static void __init smp_boot_cpus(unsigned int max_cpus)
        cpu_set(0, cpu_sibling_map[0]);
        cpu_set(0, cpu_core_map[0]);
 
+       smpboot_setup_io_apic();
+
+       setup_boot_APIC_clock();
+
        /*
         * Synchronize the TSC with the AP
         */
index 07471bba2dc6a7d10b28a09ef0c2ae119444f797..41c5b2dc6200b0a600a830f42121e2290d5b29d6 100644 (file)
@@ -440,8 +440,8 @@ static int time_init_device(void)
 
 device_initcall(time_init_device);
 
-extern void (*late_time_init)(void);
 #ifdef CONFIG_HPET_TIMER
+extern void (*late_time_init)(void);
 /* Duplicate of time_init() below, with hpet_enable part added */
 static void __init hpet_time_init(void)
 {
@@ -458,11 +458,6 @@ static void __init hpet_time_init(void)
        printk(KERN_INFO "Using %s for high-res timesource\n",cur_timer->name);
 
        time_init_hook();
-
-#ifdef CONFIG_X86_LOCAL_APIC
-       if (enable_local_apic >= 0)
-               APIC_late_time_init();
-#endif
 }
 #endif
 
@@ -487,9 +482,4 @@ void __init time_init(void)
        printk(KERN_INFO "Using %s for high-res timesource\n",cur_timer->name);
 
        time_init_hook();
-
-#ifdef CONFIG_X86_LOCAL_APIC
-       if (enable_local_apic >= 0)
-               late_time_init = APIC_late_time_init;
-#endif
 }
index 330fd2b6807574afd159e968fbf7b043c978a2af..3984226a8b98f432ec83a1222c4701a671de01e5 100644 (file)
@@ -398,7 +398,7 @@ DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pci_fixup_video);
  */
 static u16 toshiba_line_size;
 
-static struct dmi_system_id __devinit toshiba_ohci1394_dmi_table[] = {
+static struct dmi_system_id __devinitdata toshiba_ohci1394_dmi_table[] = {
        {
                .ident = "Toshiba PS5 based laptop",
                .matches = {
index 708634b685e44ff925bb2d4eca7af5de8173e329..cb76916b014dd49fd0b333951931d3f8192c367d 100644 (file)
@@ -15,7 +15,7 @@
 #include <linux/irq.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/system.h>
 #include <asm/m32r.h>
index 4e709809efc5f98ec1a04771a265dd33d2b5ea6d..501d798cf0508811a4d7fd8e1754e36c40aa9895 100644 (file)
@@ -11,7 +11,7 @@
 #include <linux/irq.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/system.h>
 #include <asm/m32r.h>
index a1d801598aa44b8a449196025fb3d551e55a3a9a..7f2db5bfd626de1eefac4a297c4ab45116563e42 100644 (file)
@@ -11,7 +11,7 @@
 #include <linux/irq.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/system.h>
 #include <asm/m32r.h>
index a76412e883e8cecfb79396fe9ab26e6e0d6d7175..9c79341a7b455711c165da735893f61cad8cef37 100644 (file)
@@ -11,7 +11,7 @@
 #include <linux/irq.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/system.h>
 #include <asm/m32r.h>
index d7b7ec6d30f88c3942eec68d520b05b0a66675a7..1fbb140854e7b728d3c7ecc0f8344809010fee0b 100644 (file)
@@ -16,7 +16,7 @@
 #include <linux/irq.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/system.h>
 #include <asm/m32r.h>
index 1f7b465c8038bab56d5d7d1346544eeb9cc6b733..48d3f54f88f88e23bfb4a134cb9a2fd4c12416c8 100644 (file)
@@ -9,6 +9,7 @@
  */
 #include <linux/config.h>
 #include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/resource.h>
index 4712de8ff80fd3b750a7f54b9db95578be418c53..65ac0b9c2d05d8c9ccce0ec77af579d4066205bf 100644 (file)
@@ -14,6 +14,7 @@
  */
 #include <linux/init.h>
 #include <linux/module.h>
+#include <linux/platform_device.h>
 #include <platforms/4xx/ibm440ep.h>
 #include <asm/ocp.h>
 #include <asm/ppc4xx_pic.h>
index d90627b68faa5580516d0419fcac699fc3a5e3c8..7e33bb635443e8c2ad459d5cb0a605691d23cb9a 100644 (file)
@@ -10,6 +10,7 @@
  */
 
 #include <linux/init.h>
+#include <linux/platform_device.h>
 #include <asm/ocp.h>
 #include <asm/ppc4xx_pic.h>
 #include <platforms/4xx/ibmstb4.h>
index bee8b4ac8afd221ba6c2a1b566249cb934505897..611ac861804d31f814e460a2f86d3bc6e23a317d 100644 (file)
@@ -14,7 +14,7 @@
 #include <linux/config.h>
 #include <linux/init.h>
 #include <linux/pagemap.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/ioport.h>
 #include <asm/io.h>
 #include <asm/machdep.h>
index 8b1012994dfc9ffba5297684334ef331e38a7de5..b13116691289fa19171a45803afbb04b538911c5 100644 (file)
@@ -12,7 +12,7 @@
 #include <linux/config.h>
 #include <linux/init.h>
 #include <linux/pagemap.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/ioport.h>
 #include <asm/io.h>
 #include <asm/ppc4xx_pic.h>
index a9052305c35debfcaf0c036b0cad037f9e94ea02..108a6e265185b443985e86fe95dced1108e39260 100644 (file)
@@ -13,7 +13,7 @@
 #include <linux/types.h>
 #include <linux/init.h>
 #include <linux/ioport.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/mv643xx.h>
 #include <linux/pci.h>
 
index f64ac2acb603e9b41d0b63a09d88c67df8d6ef40..6ca7bcac947421dfbdf52303bfedfdec0ae0d0f9 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/initrd.h>
 #include <linux/root_dev.h>
 #include <linux/mv643xx.h>
+#include <linux/platform_device.h>
 #include <asm/bootinfo.h>
 #include <asm/machdep.h>
 #include <asm/todc.h>
index aa50637a5cfb141fdb7aab84f941488f8557eda7..32358b3fb23654a09ece544b62a9d88b17450013 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/console.h>
 #include <linux/initrd.h>
 #include <linux/root_dev.h>
+#include <linux/platform_device.h>
 #if !defined(CONFIG_SERIAL_MPSC_CONSOLE)
 #include <linux/serial.h>
 #include <linux/tty.h>
index 53388a1c334f7dbe77a8b93c4e2b1d8e9c5ea9e9..b1324564456e80e28c220c113a4608022d9ace2e 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/bootmem.h>
 #include <linux/mtd/physmap.h>
 #include <linux/mv643xx.h>
+#include <linux/platform_device.h>
 #ifdef CONFIG_BOOTIMG
 #include <linux/bootimg.h>
 #endif
index b6a66d5e9d8357177d1701e33b9563ccbc646540..50039a204c2495f739b9e4637ba553a0f3a5f9dd 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/irq.h>
 #include <linux/ide.h>
 #include <linux/seq_file.h>
+#include <linux/platform_device.h>
 
 #include <linux/initrd.h>
 #include <linux/root_dev.h>
index a301c5ac58ddf985244fa5fd8e8fe55f93a614c3..6e58e30ceed101b1aeace5055a19d4a00583b308 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/seq_file.h>
 #include <linux/mtd/physmap.h>
 #include <linux/mv643xx.h>
+#include <linux/platform_device.h>
 #ifdef CONFIG_BOOTIMG
 #include <linux/bootimg.h>
 #endif
index 6f97911c330d4e5d02ff71f12ab2fd1262528b7f..708b8739ecdd97e1fd2d5eaed73f5e475bd6e8f6 100644 (file)
@@ -40,6 +40,7 @@
 #include <linux/serial_core.h>
 #include <linux/mv643xx.h>
 #include <linux/netdevice.h>
+#include <linux/platform_device.h>
 
 #include <asm/system.h>
 #include <asm/pgtable.h>
index ad5182efca1dd3fe7e845b2b55016c1208b71940..da3c74bfdc9271cb637c9fb90d09b4f0e93724a4 100644 (file)
@@ -15,6 +15,7 @@
 
 #include <linux/fsl_devices.h>
 #include <linux/resource.h>
+#include <linux/platform_device.h>
 #include <asm/mpc52xx.h>
 #include <asm/ppc_sys.h>
 
index a781c50d2f4caed443aa67d6433d41a8914f32fa..94ea346b7b4b8b7def5f003d5a4dfe8ce74623b4 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/string.h>
 #include <linux/spinlock.h>
 #include <linux/mv643xx.h>
+#include <linux/platform_device.h>
 
 #include <asm/byteorder.h>
 #include <asm/io.h>
index 6f88ba93412b0489e81f01d339b48c8ad4e7d577..e960fe9353256b82e611da8226988d679b2ba433 100644 (file)
@@ -13,7 +13,7 @@
 
 #include <linux/init.h>
 #include <linux/module.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/ioport.h>
 #include <asm/cpm2.h>
 #include <asm/irq.h>
index c18919941ec0f52d3f4c90e666a1c67c20d05685..1c1d65fb12df8cd1b5266ee4e7554d586af441bd 100644 (file)
@@ -13,7 +13,7 @@
 
 #include <linux/config.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/ioport.h>
 #include <asm/io.h>
 #include <asm/mach/irq.h>
index 1495007bf6c07df5061526d554382287d95e27e2..721e2601a75da2e8b8fbcbb94dc1797831bc0e7c 100644 (file)
@@ -20,6 +20,7 @@
 #include "linux/ctype.h"
 #include "linux/bootmem.h"
 #include "linux/ethtool.h"
+#include "linux/platform_device.h"
 #include "asm/uaccess.h"
 #include "user_util.h"
 #include "kern_util.h"
index f73134333f64ee55c9eb7f87521e89f577400297..b2c86257b0f83cc0f8f254d286db9bf76e4c5e86 100644 (file)
@@ -35,6 +35,7 @@
 #include "linux/blkpg.h"
 #include "linux/genhd.h"
 #include "linux/spinlock.h"
+#include "linux/platform_device.h"
 #include "asm/segment.h"
 #include "asm/uaccess.h"
 #include "asm/irq.h"
index 498d7dced1f486675a55446f96340f84b6d5dff0..0682ffd38175edd2da90dd10da2e71c8bc6d6906 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/ethtool.h>
 #include <linux/rtnetlink.h>
 #include <linux/timer.h>
+#include <linux/platform_device.h>
 
 #include <xtensa/simcall.h>
 
index 08d9cc99c7de134613a92565c9be883ccb194861..d597c922af11969213d3ea8bbe310bc57089d277 100644 (file)
@@ -10,7 +10,7 @@
  * information.
  */
 
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/dma-mapping.h>
index 5281f8e70510cda1717f2bd385e0c19353476fdd..ecacca9c877eb286bdc77eb2aea5906cc3235e18 100644 (file)
@@ -2059,10 +2059,8 @@ static void cfq_put_cfqd(struct cfq_data *cfqd)
        if (!atomic_dec_and_test(&cfqd->ref))
                return;
 
-       blk_put_queue(q);
-
        cfq_shutdown_timer_wq(cfqd);
-       q->elevator->elevator_data = NULL;
+       blk_put_queue(q);
 
        mempool_destroy(cfqd->crq_pool);
        kfree(cfqd->crq_hash);
index 00895477155e69aaac17792f253681d0e4c0a2d0..5eadbb9d4d71afdff8d74ae5e5e4f5b00c05a996 100644 (file)
@@ -177,7 +177,7 @@ static int print_unex = 1;
 #include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/devfs_fs_kernel.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/buffer_head.h> /* for invalidate_buffers() */
 
 /*
index f56b8edb06e42b217b46c9556c23ca30ef6a6063..e54f006e7e603475999cc2465278035ab547c0cf 100644 (file)
@@ -9,6 +9,7 @@
 
 static void elevator_noop_add_request(request_queue_t *q, struct request *rq)
 {
+       rq->flags |= REQ_NOMERGE;
        elv_dispatch_add_tail(q, rq);
 }
 
index 887b8b2d7882780d29bd86bfd56f9140daebbce8..d724c0de4f28334ad1de8d84ad54291f570f0a15 100644 (file)
@@ -20,7 +20,7 @@
 #include <linux/fs.h>
 #include <linux/string.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/interrupt.h>
 #include <linux/rtc.h>
 #include <linux/bcd.h>
index f86c15587238e3bfeb0d9d145325375f9a9ca94b..d05067dcea01967fa9d645b08dffc982041b91ef 100644 (file)
@@ -48,6 +48,7 @@
 #include <linux/dmi.h>
 #include <linux/err.h>
 #include <linux/kfifo.h>
+#include <linux/platform_device.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
index eb7058cbf015545ee032fa9d67128fa505d13599..24355b23b2ca8a3aacf77937dd829e66105fac0b 100644 (file)
@@ -17,7 +17,7 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/fs.h>
 #include <linux/init.h>
 #include <linux/module.h>
index 683278bc5241f1e9a14ab8412ea8368aa4bc8855..94641085faf8b66a93ab1c9add8d2cdf49297c1b 100644 (file)
@@ -19,7 +19,7 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
 #include <linux/init.h>
index a6dbe4da030c7712fa69b90d0f89de618428d55c..5e3292df69d8d794a1a0c4a5ccf5f1becbb38591 100644 (file)
@@ -17,7 +17,7 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/fs.h>
 #include <linux/init.h>
 #include <linux/ioport.h>
index 75ca84ed4adf5ea728bfa3c05d23bcdaae4fc72c..da631c114fd17089e4ad475377e813bfa8f9618a 100644 (file)
@@ -29,7 +29,7 @@
 #include <linux/reboot.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/hardware/arm_twd.h>
 #include <asm/uaccess.h>
@@ -396,6 +396,7 @@ static int __devexit mpcore_wdt_remove(struct device *dev)
 }
 
 static struct device_driver mpcore_wdt_driver = {
+       .owner          = THIS_MODULE,
        .name           = "mpcore_wdt",
        .bus            = &platform_bus_type,
        .probe          = mpcore_wdt_probe,
index 6d3ff0836c440b90b61697e2bc2c4559f45b085b..119b3c541d9510077e14598409bf7c8ac3cc261f 100644 (file)
@@ -22,6 +22,8 @@
 #include <linux/miscdevice.h>
 #include <linux/module.h>
 #include <linux/watchdog.h>
+#include <linux/platform_device.h>
+
 #include <asm/mv64x60.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
@@ -211,6 +213,7 @@ static int __devexit mv64x60_wdt_remove(struct device *dev)
 }
 
 static struct device_driver mv64x60_wdt_driver = {
+       .owner = THIS_MODULE,
        .name = MV64x60_WDT_NAME,
        .bus = &platform_bus_type,
        .probe = mv64x60_wdt_probe,
index 5308e5c8f29af139867d36436ccb32314d05cde6..d9ef55bdf88a2c141ca3c0c9cb629092e1094635 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *     Berkshire PCI-PC Watchdog Card Driver
  *
- *     (c) Copyright 2003 Wim Van Sebroeck <wim@iguana.be>.
+ *     (c) Copyright 2003-2005 Wim Van Sebroeck <wim@iguana.be>.
  *
  *     Based on source code of the following authors:
  *       Ken Hollis <kenji@bitgate.com>,
@@ -21,7 +21,9 @@
  */
 
 /*
- *     A bells and whistles driver is available from http://www.pcwd.de/
+ *     A bells and whistles driver is available from: 
+ *     http://www.kernel.org/pub/linux/kernel/people/wim/pcwd/pcwd_pci/
+ *
  *     More info available at http://www.berkprod.com/ or http://www.pcwatchdog.com/
  */
 
index b732020acadb1962fa693d5346e97dec33c0acfa..751cb77b0715531803d85010e5a09529f55d124c 100644 (file)
@@ -44,7 +44,7 @@
 #include <linux/watchdog.h>
 #include <linux/fs.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/interrupt.h>
 
 #include <asm/uaccess.h>
@@ -497,6 +497,7 @@ static int s3c2410wdt_resume(struct device *dev)
 
 
 static struct device_driver s3c2410wdt_driver = {
+       .owner          = THIS_MODULE,
        .name           = "s3c2410-wdt",
        .bus            = &platform_bus_type,
        .probe          = s3c2410wdt_probe,
index b5d8210154216bec46da66ae16ede0793746010f..d15ca9a3986f03ab159102538a585d6b592eea9f 100644 (file)
@@ -359,5 +359,5 @@ module_exit(wdt_exit);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Pádraig Brady <P@draigBrady.com>");
-MODULE_DESCRIPTION("w38627hf WDT driver");
+MODULE_DESCRIPTION("w83627hf WDT driver");
 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index 15677f20bd85bd9208a41610eea9a2b7b6e70ee3..0f97a0cb0ff431733f27551ffaa779668143000e 100644 (file)
@@ -9,7 +9,7 @@
 
 #include <linux/config.h>
 #include <linux/kernel.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/eisa.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
index 955537fe9958acd177a6d8dd1f15b9e0b054bcb7..8ed6ddbb9c5d5f1932bb3ff7ca756f37aa39a552 100644 (file)
@@ -20,7 +20,7 @@
  *  GNU General Public License for more details.
  */
 
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 #include <linux/errno.h>
 #include <linux/init.h>
index 4f4ba9b6d1821bce9a53bbdc4470d2caba99c16e..125929c9048f0be47b4e59543d79a5b62a0e0ff6 100644 (file)
@@ -41,7 +41,7 @@
 #include <linux/string.h>
 #include <linux/errno.h>
 #include <linux/blkdev.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/spinlock.h>
 #include <linux/moduleparam.h>
 #include <linux/firmware.h>
index 0015da5668a181aa8f9956cc59539c49483c3d95..1e5dfc7805e289acf2063f95774bf387e6ab4876 100644 (file)
@@ -27,7 +27,7 @@
  */
 
 #include <linux/delay.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/input.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
index 53c6494451020d88613c7f229f04fa043c329b0e..cfae4ad00faee2bb6ca81b1ed34a7c71f1c66126 100644 (file)
@@ -35,7 +35,7 @@
 #include <linux/init.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/i2c.h>
 
 #include <asm/io.h>
index 4fdc02411609208310520bf91b7d55875317ad9f..03672c9ca4092ade4a0590d598b38a9fda259147 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/errno.h>
 #include <linux/i2c.h>
 #include <linux/i2c-isa.h>
+#include <linux/platform_device.h>
 
 static u32 isa_func(struct i2c_adapter *adapter);
 
index 42016ee6ef133908f98549199c41a8f6d93f6096..64552a376f2d000291690abe8d4d4d685a768daf 100644 (file)
@@ -28,7 +28,7 @@
 
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/module.h>
 #include <linux/i2c.h>
 #include <linux/i2c-algo-bit.h>
index 69303ab65e0488d91799bb509ce95c10e6ae803a..cc652c3508149b73ed03ed3ef990c0365efd3fcc 100644 (file)
@@ -28,7 +28,7 @@
 
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/module.h>
 #include <linux/i2c.h>
 #include <linux/i2c-algo-bit.h>
index 8491633005b87f007607e0407a5072e0c2c25c96..65b939a059e9b6fd803cacc7699533b3fd58b559 100644 (file)
@@ -19,6 +19,8 @@
 #include <linux/sched.h>
 #include <linux/init.h>
 #include <linux/pci.h>
+#include <linux/platform_device.h>
+
 #include <asm/io.h>
 #include <linux/fsl_devices.h>
 #include <linux/i2c.h>
index d0d2a6f1386e03dcd5ea64ec9e83ca2b4d2dfc55..6b48027b2ee340b9cdab8624c03f7c029abea6b2 100644 (file)
@@ -17,6 +17,8 @@
 #include <linux/i2c.h>
 #include <linux/interrupt.h>
 #include <linux/mv643xx.h>
+#include <linux/platform_device.h>
+
 #include <asm/io.h>
 
 /* Register defines */
index 44b595d90a4a19cd8554ba9a04ebfbc4af9455c2..67ccbea24ba4b54d546f794b385798d5e193d4a1 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/errno.h>
 #include <linux/interrupt.h>
 #include <linux/i2c-pxa.h>
+#include <linux/platform_device.h>
 
 #include <asm/hardware.h>
 #include <asm/irq.h>
index 6ced28e90070a90d6cf3eb7ebd1ad4d9653e30b2..1b582262e677ed89ce68b9277e927d4f61046948 100644 (file)
@@ -33,7 +33,7 @@
 #include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/err.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/hardware.h>
 #include <asm/irq.h>
@@ -918,8 +918,11 @@ static int __init i2c_adap_s3c_init(void)
        int ret;
 
        ret = driver_register(&s3c2410_i2c_driver);
-       if (ret == 0)
-               ret = driver_register(&s3c2440_i2c_driver); 
+       if (ret == 0) {
+               ret = driver_register(&s3c2440_i2c_driver);
+               if (ret)
+                       driver_unregister(&s3c2410_i2c_driver);
+       }
 
        return ret;
 }
index eaa4742e04fa661bda29b62992a58a17ce52a6e0..9dbb72fffbe2520fdcc47d220cb272adf4a81be4 100644 (file)
@@ -27,7 +27,7 @@
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/interrupt.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/usb_ch9.h>
 #include <linux/usb_gadget.h>
 #include <linux/usb.h>
index 02e335a04f095ad546c7689a1cb5ad3abfb06fda..82ea1b7ec9145536439467778300e29dd1ac0e1d 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/init.h>
 #include <linux/idr.h>
 #include <linux/seq_file.h>
+#include <linux/platform_device.h>
 #include <asm/uaccess.h>
 
 
index ea14c8f1c82baa58b7df2e12921e36c720c1bf85..8af0bd1424d21574f6c284bcdd6ea79ed2f92c75 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/init.h>
 #include <linux/i2c.h>
 #include <linux/i2c-dev.h>
+#include <linux/platform_device.h>
 #include <asm/uaccess.h>
 
 static struct i2c_client i2cdev_client_template;
index a4696cd0978ca68c436ad37ceaa66252b4464b68..9f2352bd8348efa34047125a4f72c181dd5d9f93 100644 (file)
@@ -565,6 +565,7 @@ static long evdev_ioctl_compat(struct file *file, unsigned int cmd, unsigned lon
                                                case EV_LED: bits = dev->ledbit; max = LED_MAX; break;
                                                case EV_SND: bits = dev->sndbit; max = SND_MAX; break;
                                                case EV_FF:  bits = dev->ffbit;  max = FF_MAX;  break;
+                                               case EV_SW:  bits = dev->swbit;  max = SW_MAX;  break;
                                                default: return -EINVAL;
                                        }
                                        bit_to_user(bits, max);
@@ -579,6 +580,9 @@ static long evdev_ioctl_compat(struct file *file, unsigned int cmd, unsigned lon
                                if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0)))
                                        bit_to_user(dev->snd, SND_MAX);
 
+                               if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSW(0)))
+                                       bit_to_user(dev->sw, SW_MAX);
+
                                if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0))) {
                                        int len;
                                        if (!dev->name) return -ENOENT;
index 3b1685ff9d10b38db94defa977cf1e6c43effc38..1a1654caedd53a2ca59390bae8e97791b41298b6 100644 (file)
@@ -730,7 +730,7 @@ static void input_register_classdevice(struct input_dev *dev)
                 "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
 
        path = kobject_get_path(&dev->cdev.class->subsys.kset.kobj, GFP_KERNEL);
-       printk(KERN_INFO "input: %s/%s as %s\n",
+       printk(KERN_INFO "input: %s as %s/%s\n",
                dev->name ? dev->name : "Unspecified device",
                path ? path : "", dev->cdev.class_id);
        kfree(path);
index 571a68691a4ae3f4ce7d27db795696f2491c575d..4a917748fd9ffac79f863675574ffd2155994858 100644 (file)
@@ -13,11 +13,11 @@ menuconfig INPUT_KEYBOARD
 if INPUT_KEYBOARD
 
 config KEYBOARD_ATKBD
-       tristate "AT keyboard" if !PC
+       tristate "AT keyboard" if !X86_PC
        default y
        select SERIO
        select SERIO_LIBPS2
-       select SERIO_I8042 if PC
+       select SERIO_I8042 if X86_PC
        select SERIO_GSCPS2 if GSC
        help
          Say Y here if you want to use a standard AT or PS/2 keyboard. Usually
index 3210d298b3bc01d6e984b89097962876cc27db2e..d00d14bb637a70b9a49125fa308413efd14eca60 100644 (file)
@@ -12,7 +12,7 @@
  */
 
 #include <linux/delay.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/init.h>
 #include <linux/input.h>
 #include <linux/interrupt.h>
index 7f06780a437f5d435bfd27ff45e459a0b8d56021..9481132532d017ee42d36cecc789a0007e7bca2b 100644 (file)
@@ -441,7 +441,7 @@ lkkbd_interrupt (struct serio *serio, unsigned char data, unsigned int flags,
                        input_sync (lk->dev);
                        break;
                case LK_METRONOME:
-                       DBG (KERN_INFO "Got %#d and don't "
+                       DBG (KERN_INFO "Got LK_METRONOME and don't "
                                        "know how to handle...\n");
                        break;
                case LK_OUTPUT_ERROR:
index cee9c734a048c9102eaeec0090c99a6e7a8656f0..0fa38a559cdfedc0815a4d91f521f111b08fd86d 100644 (file)
@@ -12,7 +12,7 @@
  */
 
 #include <linux/delay.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/init.h>
 #include <linux/input.h>
 #include <linux/interrupt.h>
index e34633c37fddc5fb52ec27c68b7d146032e91a69..68ac97f101b0369a6f7a358829a3d6fd25dc4910 100644 (file)
@@ -71,7 +71,7 @@ static int __init pcspkr_init(void)
                return -ENOMEM;
 
        pcspkr_dev->name = "PC Speaker";
-       pcspkr_dev->name = "isa0061/input0";
+       pcspkr_dev->phys = "isa0061/input0";
        pcspkr_dev->id.bustype = BUS_ISA;
        pcspkr_dev->id.vendor = 0x001f;
        pcspkr_dev->id.product = 0x0001;
index 537154dd7a873cc8e252bc2cf61f87f01395936e..574b18a523af2df86eaedb9def31a9d629ae1aad 100644 (file)
@@ -17,7 +17,7 @@ config MOUSE_PS2
        default y
        select SERIO
        select SERIO_LIBPS2
-       select SERIO_I8042 if PC
+       select SERIO_I8042 if X86_PC
        select SERIO_GSCPS2 if GSC
        ---help---
          Say Y here if you have a PS/2 mouse connected to your system. This
index dd0f5bd902413bf89810c5c727775c58c421efce..4da6c86b5d76a46979104b2dcc87edadd0cde7e8 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/serio.h>
 #include <linux/errno.h>
 #include <linux/err.h>
+#include <linux/platform_device.h>
 
 #include <asm/io.h>
 
index 4bc40f15999603c5f30e9184575078d5c5a56fcb..01e186422021d3a5c594f5b59a376543b817b2e5 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/serio.h>
 #include <linux/err.h>
 #include <linux/rcupdate.h>
+#include <linux/platform_device.h>
 
 #include <asm/io.h>
 
index 9880fc145d9051c1be9a02b9d4fe7f70ec76e53d..d857f7081adb03e408778dc0f7ce058e79df08e3 100644 (file)
@@ -14,7 +14,7 @@
 #include <linux/interrupt.h>
 #include <linux/ioport.h>
 #include <linux/delay.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/err.h>
index 46093c507988e002fa8ee8abf198cd397721d396..b44d255596c25db08eb1c184c29f4f4468e08f14 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/interrupt.h>
 #include <linux/err.h>
 #include <linux/bitops.h>
+#include <linux/platform_device.h>
 
 #include <asm/io.h>
 #include <asm/uaccess.h>
index 106f5eefd89a256bae649d02b7c46a853da58f8c..52c49258f8a4e4dc447dd5a98d89182e38d2b395 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/init.h>
 #include <linux/serio.h>
 #include <linux/err.h>
+#include <linux/platform_device.h>
 
 #include <asm/irq.h>
 #include <asm/hardware.h>
index 0ba3e6562bffeb8762230891ed580904a5a6080b..15e88eeae8d6daf1cacd4216080cffbc7b09f258 100644 (file)
@@ -11,7 +11,7 @@
 
 
 #include <linux/delay.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/init.h>
 #include <linux/input.h>
 #include <linux/interrupt.h>
index cdb6d028319577ad6dc0293a66c87ad5538872f2..8f02c155fdc0e26693ce5babe3abe1b5e14221be 100644 (file)
@@ -723,6 +723,7 @@ adbhid_input_register(int id, int default_id, int original_handler_id,
 
        sprintf(hid->phys, "adb%d:%d.%02x/input", id, default_id, original_handler_id);
 
+       hid->input = input_dev;
        hid->id = default_id;
        hid->original_handler_id = original_handler_id;
        hid->current_handler_id = current_handler_id;
index 720e7a3263088b881dce613e958af67f0f243586..7daa0ed7331cd9a46ee537f1939af2bf464e2b0e 100644 (file)
@@ -18,7 +18,7 @@
 #include <linux/delay.h>
 #include <linux/spinlock.h>
 #include <linux/slab.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/dma.h>
 #include <asm/hardware.h>
index 46de5c9405557b0ddb1166f397539b94742bc38b..9c4dd682ac74f1fcf92fab62e3b666fd2d54afb9 100644 (file)
@@ -21,7 +21,7 @@
 #include <linux/miscdevice.h>
 #include <linux/pci.h>
 #include <linux/proc_fs.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <asm/uaccess.h>
 #include <linux/hdpu_features.h>
 
index c203b27269ea7278437676d863d17445baebf2d8..165f3405df277f3ffef0f88a3028789b0e76e4b2 100644 (file)
@@ -21,7 +21,7 @@
 #include <linux/hdpu_features.h>
 #include <linux/pci.h>
 
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 static int hdpu_nexus_probe(struct device *ddev);
 static int hdpu_nexus_remove(struct device *ddev);
index d575e3a018bc109365d37153b4ecd305b6966789..f31e247b2cbeabf479c912ae896453db70278935 100644 (file)
@@ -20,7 +20,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/ioport.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/dma-mapping.h>
index 3ace875decc4475270aa8abc066471a3abd634bf..942668e93a7460603251e1fbaa567a4152d36cb4 100644 (file)
@@ -26,7 +26,7 @@
 #include <linux/moduleparam.h>
 #include <linux/init.h>
 #include <linux/ioport.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/interrupt.h>
 #include <linux/dma-mapping.h>
 #include <linux/delay.h>
index 63104c73ca3c8bb7caadb6e3b038318fcffd06ec..bfe994e59265d57772da814b0b08eab199e2e793 100644 (file)
@@ -34,7 +34,7 @@
 #include <linux/ioport.h>
 #include <linux/device.h>
 #include <linux/slab.h>
-
+#include <linux/platform_device.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
 #include <linux/mtd/partitions.h>
index e39a98a0171c801b44b0eec73730d2ebb8df5120..d14a0185b8f419f97d14595caee88803c64769d9 100644 (file)
@@ -32,7 +32,7 @@
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/ioport.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/init.h>
 
 #include <linux/mtd/mtd.h>
index 1e5d6e1d05f318c1f0e45c866ed465a825fe6b6c..00b9f67580f1566550950327479fd59af1ca2a33 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/slab.h>
 #include <linux/ioport.h>
 #include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
index da316e543237676e8fdf61a6a98923f06d27adca..733a9297a56263eda2c49bfe7db2545f86ed78d6 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/slab.h>
 #include <linux/ioport.h>
 #include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
index fa84566245a7093588a53df13f30774a4d323591..7f370bb794fefae45e192df190d657e7bcd13110 100644 (file)
@@ -30,7 +30,7 @@
  * 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
index a0577ea00c3c974bb1121d5a11d9cac0765c82b4..104576b5be3480cf238f1f4c3ac0a6c9bbfeca8f 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/ioport.h>
 #include <linux/device.h>
 #include <linux/slab.h>
+#include <linux/platform_device.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
index c81bec7b14d5565c97deed108be3155beec98fdd..c8d0da19d897e461a18efaace5076bfc12111d59 100644 (file)
@@ -13,7 +13,7 @@
 #include <linux/init.h>
 #include <linux/errno.h>
 #include <linux/slab.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/err.h>
 
 #include <linux/mtd/mtd.h>
index b58ba236a9eb8a5ffd479da0b56d88a041acc860..2df5e47d1f5ce2ec7c16a9ab6018d7e4be6c1eff 100644 (file)
@@ -48,7 +48,7 @@
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/ioport.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/slab.h>
index c4aa5fe2840e721199d1eac7e1bd32978415d480..4d26e5e7d18b39e9dc411e080f1f380c9970296a 100644 (file)
 #include <linux/unistd.h>
 #include <linux/ctype.h>
 #include <linux/moduleparam.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/bitops.h>
 
 #include <asm/uaccess.h>
index abce1f730d00b32df657af0bbbca5525e903eb51..c0af6fb1fbba210c3139bb18f66ad7960afd8780 100644 (file)
@@ -66,6 +66,7 @@
 #include <linux/mii.h>
 #include <linux/dm9000.h>
 #include <linux/delay.h>
+#include <linux/platform_device.h>
 
 #include <asm/delay.h>
 #include <asm/irq.h>
index ae5a2ed3b2640336a656087596206d2e462ee73e..962580f2c4abb2b5fbdcf21e35db626df2400946 100644 (file)
@@ -81,7 +81,7 @@
 #include <linux/if_vlan.h>
 #include <linux/spinlock.h>
 #include <linux/mm.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/ip.h>
 #include <linux/tcp.h>
 #include <linux/udp.h>
index 1eca1dbca7f1a10c94cb5bd6ae95a5cac8f35998..5a74d3d3dbe1763befa68f4823ba4d7797def74d 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/version.h>
+#include <linux/platform_device.h>
 #include <asm/ocp.h>
 #include <linux/crc32.h>
 #include <linux/mii.h>
index b886b07412a67c95f8e64b5203af7ad92722121e..e1aa9910503bf08bf844f8327f26a5419eb9b939 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/rtnetlink.h>
 #include <linux/interrupt.h>
 #include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
 #include <linux/pm.h>
 
 #include <net/irda/irda.h>
index 06883309916d493636097dddc1e5b15e93359cf5..76e0b9fb5e96a906c9a5ba915d02407ef3b5414e 100644 (file)
@@ -29,7 +29,7 @@
 #include <linux/rtnetlink.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 
 #include <net/irda/irda.h>
index 140b7cdb1f7e8186f43e896a75a0ecf09af9278a..a1d207f2fa68e0c26f54267539ab169dfd40bff0 100644 (file)
@@ -53,6 +53,7 @@
 #include <linux/rtnetlink.h>
 #include <linux/serial_reg.h>
 #include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
 
 #include <asm/io.h>
 #include <asm/dma.h>
index 8423cb6875f06d75ac38f18881199a5f2b8acb41..a74a5cfaf5bc821271e6c1ae2087b1ed6e38a039 100644 (file)
@@ -33,7 +33,7 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 
 #include <asm/bootinfo.h>
index 405e18365edef4c353b681937ebf63eb3971def7..e9c999d7eb390103a26031a5c782065cba883c81 100644 (file)
@@ -47,7 +47,7 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 
 #include <asm/bootinfo.h>
index f79f7ee72ab89488734bcaba2fd9caf4731ab374..bbffb585b3b383a4582c22ab2078dafd471f1f14 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/sched.h>
 #include <linux/etherdevice.h>
 #include <linux/netdevice.h>
+#include <linux/platform_device.h>
 #include <asm/io.h>
 #include <asm/mips-boards/simint.h>
 
index 8fbba21d975bc4ad7cbde8dcb9d1998acf3501a3..71f2c6705bc32ba018788e0283c76e71871af507 100644 (file)
@@ -39,6 +39,8 @@
 #include <linux/bitops.h>
 #include <linux/delay.h>
 #include <linux/ethtool.h>
+#include <linux/platform_device.h>
+
 #include <asm/io.h>
 #include <asm/types.h>
 #include <asm/pgtable.h>
index c573bb351d4c9bb11ce15876b7b58e98373cc933..74d5f1a6fdea262919736ebd9c86f45cd05d0a6e 100644 (file)
@@ -77,7 +77,7 @@ static const char version[] =
 #include <linux/errno.h>
 #include <linux/ioport.h>
 #include <linux/crc32.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/spinlock.h>
 #include <linux/ethtool.h>
 #include <linux/mii.h>
index eb1423ede75cae8032a0729fdb115e54fa9e7c86..d04c918ebef811ad60af0bdede6d5d1047ccb446 100644 (file)
@@ -29,6 +29,7 @@ static const char version[] = "proteon.c: v1.00 02/01/2003 by Jochen Friedrich\n
 #include <linux/init.h>
 #include <linux/netdevice.h>
 #include <linux/trdevice.h>
+#include <linux/platform_device.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
index 3c7c66204f7474d0005021afc7fe740698fc2d3a..72cf708396be3f87e9badd604ee3b5b3ab41c6a2 100644 (file)
@@ -36,6 +36,7 @@ static const char version[] = "skisa.c: v1.03 09/12/2002 by Jochen Friedrich\n";
 #include <linux/init.h>
 #include <linux/netdevice.h>
 #include <linux/trdevice.h>
+#include <linux/platform_device.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
index ba48cef3a9dc87faeffa7ba3cc7d355176a6a577..87302c548c24d437fa0722a01d2d832c7a7bdf38 100644 (file)
@@ -42,7 +42,7 @@
 #include <linux/notifier.h>
 #include <linux/interrupt.h>
 #include <linux/spinlock.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
index b57a0b98b4d6b89c521c9172463b3ffc342f9aec..561706ba4499a8fc99eae63081b88212375d222c 100644 (file)
@@ -37,7 +37,7 @@
 #include <asm/errno.h>
 #include <linux/irq.h>
 #include <linux/interrupt.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/io.h>
 #include <asm/hd64465/hd64465.h>
index 4a41f67d185d9f5b181ced34f110d627433f2ce3..7ce455d01cc919a761ecd5de44bfc504c6246714 100644 (file)
@@ -47,7 +47,7 @@
 #include <linux/delay.h>
 #include <linux/workqueue.h>
 #include <linux/interrupt.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/bitops.h>
 #include <asm/irq.h>
 #include <asm/io.h>
index c6ed70ea4812ec557a41d65469e31180c775cfc8..2c22b4b3619d58f6c1c1a5c107597eb724b2a7e5 100644 (file)
@@ -23,7 +23,7 @@
 #include <linux/delay.h>
 #include <linux/workqueue.h>
 #include <linux/interrupt.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/bitops.h>
 #include <asm/irq.h>
 #include <asm/io.h>
index 3397ff28de6aa7897963dd3d6a64f8e68483f821..356a6fb416a14010c86c43df9927088038085a50 100644 (file)
@@ -23,7 +23,7 @@
 #include <linux/delay.h>
 #include <linux/workqueue.h>
 #include <linux/interrupt.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <asm/irq.h>
 #include <asm/io.h>
 #include <asm/bitops.h>
index 2558c3cc91eca9eef0a6239c402a8c1afcbb092e..47b5ade95bde5ae8093997c208f79ed242a4aaa7 100644 (file)
@@ -12,7 +12,7 @@
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/errno.h>
 #include <linux/init.h>
 #include <linux/delay.h>
index c2a12d53f6c76e9afd8341174db3bb40b79f47c2..7fa18fb814bc7b862478e38c4d3b3a42f3e79b40 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/ioport.h>
 #include <linux/kernel.h>
 #include <linux/spinlock.h>
+#include <linux/platform_device.h>
 
 #include <asm/hardware.h>
 #include <asm/io.h>
index bbe69b07ce50406a1ab6fb3b23901cdca40b90ec..5209d8c7764fc13215135a9d6bfb3f0828ca380e 100644 (file)
@@ -17,7 +17,7 @@
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/interrupt.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <pcmcia/ss.h>
 
index bd924336a49fe041333745766c3358a1ecc020d4..fe5ea36e7de3878e63b7819cab7872c89d8acd49 100644 (file)
@@ -16,7 +16,7 @@
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/interrupt.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/mach-types.h>
 #include <asm/hardware.h>
index acf60ffc8a124515dbe4cbd42355c647b8414ff0..6d441ec75c6a14bc1fa34e9b31cd866f1c6a8397 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/config.h>
+#include <linux/platform_device.h>
 
 #include <pcmcia/cs_types.h>
 #include <pcmcia/cs.h>
index f158b67f661007c7b9a1ef64fd55a08f9f70d154..e312638643771c27bb75ce8f81c823b809a51a2b 100644 (file)
@@ -44,7 +44,7 @@
 #include <linux/ioport.h>
 #include <linux/delay.h>
 #include <linux/workqueue.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/bitops.h>
 
 #include <asm/io.h>
index 3d2dca675e02bc9483b8b8828d2ee948f7e6f52c..38a028c725d47e0aaebe529a6a33ff1e24710357 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/spinlock.h>
 #include <linux/sched.h>
 #include <linux/types.h>
+#include <linux/platform_device.h>
 
 #include <asm/io.h>
 
index f24d84538fd56ab5c1d2cdd8df3c509fac8e5092..71dd1ebbe58f26b2740cd77a545e31d6c7472a01 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/init.h>
 #include <linux/completion.h>
 #include <linux/transport_class.h>
+#include <linux/platform_device.h>
 
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_host.h>
index 00d6a6657ebc3ec78f19d28e25edb2e3354513d4..a440ea38efaa473c11ff379b8564604ca64d669c 100644 (file)
@@ -180,12 +180,22 @@ static void idescsi_input_buffers (ide_drive_t *drive, idescsi_pc_t *pc, unsigne
                        return;
                }
                count = min(pc->sg->length - pc->b_count, bcount);
-               buf = kmap_atomic(pc->sg->page, KM_IRQ0);
-               drive->hwif->atapi_input_bytes(drive,
-                               buf + pc->b_count + pc->sg->offset, count);
-               kunmap_atomic(buf, KM_IRQ0);
-               bcount -= count;
-               pc->b_count += count;
+               if (PageHighMem(pc->sg->page)) {
+                       unsigned long flags;
+
+                       local_irq_save(flags);
+                       buf = kmap_atomic(pc->sg->page, KM_IRQ0) +
+                                       pc->sg->offset;
+                       drive->hwif->atapi_input_bytes(drive,
+                                               buf + pc->b_count, count);
+                       kunmap_atomic(buf - pc->sg->offset, KM_IRQ0);
+                       local_irq_restore(flags);
+               } else {
+                       buf = page_address(pc->sg->page) + pc->sg->offset;
+                       drive->hwif->atapi_input_bytes(drive,
+                                               buf + pc->b_count, count);
+               }
+               bcount -= count; pc->b_count += count;
                if (pc->b_count == pc->sg->length) {
                        pc->sg++;
                        pc->b_count = 0;
@@ -205,12 +215,22 @@ static void idescsi_output_buffers (ide_drive_t *drive, idescsi_pc_t *pc, unsign
                        return;
                }
                count = min(pc->sg->length - pc->b_count, bcount);
-               buf = kmap_atomic(pc->sg->page, KM_IRQ0);
-               drive->hwif->atapi_output_bytes(drive,
-                               buf + pc->b_count + pc->sg->offset, count);
-               kunmap_atomic(buf, KM_IRQ0);
-               bcount -= count;
-               pc->b_count += count;
+               if (PageHighMem(pc->sg->page)) {
+                       unsigned long flags;
+
+                       local_irq_save(flags);
+                       buf = kmap_atomic(pc->sg->page, KM_IRQ0) +
+                                               pc->sg->offset;
+                       drive->hwif->atapi_output_bytes(drive,
+                                               buf + pc->b_count, count);
+                       kunmap_atomic(buf - pc->sg->offset, KM_IRQ0);
+                       local_irq_restore(flags);
+               } else {
+                       buf = page_address(pc->sg->page) + pc->sg->offset;
+                       drive->hwif->atapi_output_bytes(drive,
+                                               buf + pc->b_count, count);
+               }
+               bcount -= count; pc->b_count += count;
                if (pc->b_count == pc->sg->length) {
                        pc->sg++;
                        pc->b_count = 0;
index 8be7dc0b47b849858c8d5b37872e7112fb8e3b31..ff18fa7044c593ebe61ebd184cc45ffbbc91b771 100644 (file)
@@ -294,28 +294,6 @@ void ata_exec_command(struct ata_port *ap, const struct ata_taskfile *tf)
                ata_exec_command_pio(ap, tf);
 }
 
-/**
- *     ata_exec - issue ATA command to host controller
- *     @ap: port to which command is being issued
- *     @tf: ATA taskfile register set
- *
- *     Issues PIO/MMIO write to ATA command register, with proper
- *     synchronization with interrupt handler / other threads.
- *
- *     LOCKING:
- *     Obtains host_set lock.
- */
-
-static inline void ata_exec(struct ata_port *ap, const struct ata_taskfile *tf)
-{
-       unsigned long flags;
-
-       DPRINTK("ata%u: cmd 0x%X\n", ap->id, tf->command);
-       spin_lock_irqsave(&ap->host_set->lock, flags);
-       ap->ops->exec_command(ap, tf);
-       spin_unlock_irqrestore(&ap->host_set->lock, flags);
-}
-
 /**
  *     ata_tf_to_host - issue ATA taskfile to host controller
  *     @ap: port to which command is being issued
@@ -326,30 +304,11 @@ static inline void ata_exec(struct ata_port *ap, const struct ata_taskfile *tf)
  *     other threads.
  *
  *     LOCKING:
- *     Obtains host_set lock.
- */
-
-static void ata_tf_to_host(struct ata_port *ap, const struct ata_taskfile *tf)
-{
-       ap->ops->tf_load(ap, tf);
-
-       ata_exec(ap, tf);
-}
-
-/**
- *     ata_tf_to_host_nolock - issue ATA taskfile to host controller
- *     @ap: port to which command is being issued
- *     @tf: ATA taskfile register set
- *
- *     Issues ATA taskfile register set to ATA host controller,
- *     with proper synchronization with interrupt handler and
- *     other threads.
- *
- *     LOCKING:
  *     spin_lock_irqsave(host_set lock)
  */
 
-void ata_tf_to_host_nolock(struct ata_port *ap, const struct ata_taskfile *tf)
+static inline void ata_tf_to_host(struct ata_port *ap,
+                                 const struct ata_taskfile *tf)
 {
        ap->ops->tf_load(ap, tf);
        ap->ops->exec_command(ap, tf);
@@ -1912,12 +1871,14 @@ static void ata_bus_post_reset(struct ata_port *ap, unsigned int devmask)
  *
  *     LOCKING:
  *     PCI/etc. bus probe sem.
+ *     Obtains host_set lock.
  *
  */
 
 static unsigned int ata_bus_edd(struct ata_port *ap)
 {
        struct ata_taskfile tf;
+       unsigned long flags;
 
        /* set up execute-device-diag (bus reset) taskfile */
        /* also, take interrupts to a known state (disabled) */
@@ -1928,7 +1889,9 @@ static unsigned int ata_bus_edd(struct ata_port *ap)
        tf.protocol = ATA_PROT_NODATA;
 
        /* do bus reset */
+       spin_lock_irqsave(&ap->host_set->lock, flags);
        ata_tf_to_host(ap, &tf);
+       spin_unlock_irqrestore(&ap->host_set->lock, flags);
 
        /* spec says at least 2ms.  but who knows with those
         * crazy ATAPI devices...
@@ -3555,7 +3518,7 @@ int ata_qc_issue_prot(struct ata_queued_cmd *qc)
 
        switch (qc->tf.protocol) {
        case ATA_PROT_NODATA:
-               ata_tf_to_host_nolock(ap, &qc->tf);
+               ata_tf_to_host(ap, &qc->tf);
                break;
 
        case ATA_PROT_DMA:
@@ -3566,20 +3529,20 @@ int ata_qc_issue_prot(struct ata_queued_cmd *qc)
 
        case ATA_PROT_PIO: /* load tf registers, initiate polling pio */
                ata_qc_set_polling(qc);
-               ata_tf_to_host_nolock(ap, &qc->tf);
+               ata_tf_to_host(ap, &qc->tf);
                ap->hsm_task_state = HSM_ST;
                queue_work(ata_wq, &ap->pio_task);
                break;
 
        case ATA_PROT_ATAPI:
                ata_qc_set_polling(qc);
-               ata_tf_to_host_nolock(ap, &qc->tf);
+               ata_tf_to_host(ap, &qc->tf);
                queue_work(ata_wq, &ap->packet_task);
                break;
 
        case ATA_PROT_ATAPI_NODATA:
                ap->flags |= ATA_FLAG_NOINTR;
-               ata_tf_to_host_nolock(ap, &qc->tf);
+               ata_tf_to_host(ap, &qc->tf);
                queue_work(ata_wq, &ap->packet_task);
                break;
 
@@ -4126,8 +4089,6 @@ static void ata_host_init(struct ata_port *ap, struct Scsi_Host *host,
        host->unique_id = ata_unique_id++;
        host->max_cmd_len = 12;
 
-       scsi_assign_lock(host, &host_set->lock);
-
        ap->flags = ATA_FLAG_PORT_DISABLED;
        ap->id = host->unique_id;
        ap->host = host;
index 1e3792f86fcf4359171d46b6ac326b099844e905..248baae9648656f762bd3f15436effee84e4511f 100644 (file)
@@ -39,6 +39,7 @@
 #include <scsi/scsi.h>
 #include "scsi.h"
 #include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
 #include <linux/libata.h>
 #include <linux/hdreg.h>
 #include <asm/uaccess.h>
@@ -2405,8 +2406,12 @@ int ata_scsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
        struct ata_port *ap;
        struct ata_device *dev;
        struct scsi_device *scsidev = cmd->device;
+       struct Scsi_Host *shost = scsidev->host;
 
-       ap = (struct ata_port *) &scsidev->host->hostdata[0];
+       ap = (struct ata_port *) &shost->hostdata[0];
+
+       spin_unlock(shost->host_lock);
+       spin_lock(&ap->host_set->lock);
 
        ata_scsi_dump_cdb(ap, cmd);
 
@@ -2429,6 +2434,8 @@ int ata_scsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
                ata_scsi_translate(ap, dev, cmd, done, atapi_xlat);
 
 out_unlock:
+       spin_unlock(&ap->host_set->lock);
+       spin_lock(shost->host_lock);
        return 0;
 }
 
index 10ecd9e15e4fb9296b8a0aa1eeacf99b5aaf2e29..fad051ca4672bf0daf6ed710cf4155f977bb7662 100644 (file)
@@ -48,7 +48,6 @@ extern int ata_qc_issue(struct ata_queued_cmd *qc);
 extern int ata_check_atapi_dma(struct ata_queued_cmd *qc);
 extern void ata_dev_select(struct ata_port *ap, unsigned int device,
                            unsigned int wait, unsigned int can_sleep);
-extern void ata_tf_to_host_nolock(struct ata_port *ap, const struct ata_taskfile *tf);
 extern void swap_buf_le16(u16 *buf, unsigned int buf_words);
 extern int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg);
 extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg);
index afb7ddf200e082d8ee6032095f50db6b4dcdd771..f47d2c454e331be473b008398ac66a9f0778ab9f 100644 (file)
@@ -33,7 +33,7 @@
 #include <linux/sysrq.h>
 #include <linux/mca.h>
 #include <linux/delay.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
 #include <linux/serial_reg.h>
index 5b3933b0c997f011897585c3cca97fd8b357c2dc..4a54ff5847003855e7643e3f57f64eb0947bb0eb 100644 (file)
@@ -36,7 +36,7 @@
 #include <linux/init.h>
 #include <linux/console.h>
 #include <linux/sysrq.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
 #include <linux/serial_core.h>
@@ -995,6 +995,7 @@ static int __init imx_serial_init(void)
 static void __exit imx_serial_exit(void)
 {
        uart_unregister_driver(&imx_reg);
+       driver_unregister(&serial_imx_driver);
 }
 
 module_init(imx_serial_init);
index 8a79968f8ce1f27b337bbdd21dc0b62c14a241d6..0dd08a09e7e693aded568a69ae827ca436534a7d 100644 (file)
@@ -45,7 +45,7 @@
  */
 
 #include <linux/config.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/module.h>
 #include <linux/tty.h>
 #include <linux/serial.h>
index aec83f577ce6c5c8f7be871af58c9db811a2d0b8..ba8838b234da7b89cfa2575a8689034b5b2cb3e9 100644 (file)
@@ -52,6 +52,8 @@
  * 4) AFAICT, hardware flow control isn't supported by the controller --MAG.
  */
 
+#include <linux/platform_device.h>
+
 #include "mpsc.h"
 
 /*
index 8cc4cedadd995066aae68c73681ea18e4827fd46..16b2f9417af9560aa040034357f06a383495b5b6 100644 (file)
@@ -39,7 +39,7 @@
 #include <linux/circ_buf.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
 #include <linux/serial_core.h>
index 06a17dff1a7316d45e2d2f729b98b99e86a8bb29..036792328d499d4f6286174855897e4bb021ece1 100644 (file)
@@ -63,7 +63,7 @@
 
 #include <linux/module.h>
 #include <linux/ioport.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/init.h>
 #include <linux/sysrq.h>
 #include <linux/console.h>
index c4a789e6af447f3c7d111409cc39972f9df1c479..ed618cc7ae96e132ffcb5e9e32cfbe8de2d22675 100644 (file)
@@ -35,7 +35,7 @@
 #include <linux/init.h>
 #include <linux/console.h>
 #include <linux/sysrq.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
 #include <linux/serial_core.h>
index 2b623ab0e36ec99f64373133e71ba907fe9b9f1b..01696b3e3f619f5b80aa9fcea4dfe4c4540790bb 100644 (file)
@@ -26,7 +26,7 @@
 #endif
 
 #include <linux/console.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/err.h>
 #include <linux/ioport.h>
 #include <linux/init.h>
index 02106bebd5c1ff147dbb070cac83b43d4003758e..975ace3f5b1eac13362d2be4f51c8514ffac5899 100644 (file)
@@ -50,7 +50,7 @@
 #include <linux/list.h>
 #include <linux/interrupt.h>
 #include <linux/version.h>
-
+#include <linux/platform_device.h>
 #include <linux/usb.h>
 #include <linux/usb_gadget.h>
 
index 9b3673904daf33aa4c4575cacac497f8c92cb01f..bc6269f10cbb5d3fd1e1f4a3f0968bea15448c38 100644 (file)
@@ -21,6 +21,8 @@
  *
  */
 
+#include <linux/platform_device.h>
+
 #include "lh7a40x_udc.h"
 
 //#define DEBUG printk
index 41c96b0afbb372b650d26a8503bcaadd2bc9699f..387692a3611e7119560b606611e75d1ac881285f 100644 (file)
@@ -38,7 +38,7 @@
 #include <linux/proc_fs.h>
 #include <linux/mm.h>
 #include <linux/moduleparam.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/usb_ch9.h>
 #include <linux/usb_gadget.h>
 #include <linux/usb_otg.h>
index f83a9262f953364de6b908a733218f67bb7fa564..ee9cd7869d9268723e00451839bb132b6e4f9440 100644 (file)
@@ -43,7 +43,7 @@
 #include <linux/interrupt.h>
 #include <linux/proc_fs.h>
 #include <linux/mm.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 
 #include <asm/byteorder.h>
index ddb8fc5914663536d45b1c3be6b056f3630e2e88..f9c3f5b8dd1c3adcdcc79dc2f129bf2278b14796 100644 (file)
@@ -70,6 +70,7 @@
 #include <linux/interrupt.h>
 #include <linux/usb.h>
 #include <linux/usb_isp116x.h>
+#include <linux/platform_device.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
index a277e258eb6c5863337a267ea1a46a50cef6caa4..f0c78cf14b6ca6b11ec98010986d54179c41a5b6 100644 (file)
@@ -18,6 +18,8 @@
  * This file is licenced under the GPL.
  */
 
+#include <linux/platform_device.h>
+
 #include <asm/mach-au1x00/au1000.h>
 
 #define USBH_ENABLE_BE (1<<0)
index 238fa4ade615cb1578853f19bda97024479515bc..336c766c6e29f0334b52da96bc72103c4b0e8767 100644 (file)
@@ -16,6 +16,8 @@
  * This file is licenced under the GPL.
  */
 
+#include <linux/platform_device.h>
+
 #include <asm/hardware.h>
 
 
index 49815ec4b842374959f3aab35fa388d290196168..e46cc540cf4d3153fb49d9b942dfdf5652dc4f76 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <linux/signal.h>      /* SA_INTERRUPT */
 #include <linux/jiffies.h>
+#include <linux/platform_device.h>
 
 #include <asm/hardware.h>
 #include <asm/io.h>
index 4832e57ae579e4d17f1361298cb54e218c75128a..92cf6f4a13748ad9535e47d2ce827c6618aa3f75 100644 (file)
@@ -14,6 +14,8 @@
  * This file is licenced under the GPL.
  */
 
+#include <linux/platform_device.h>
+
 /* configure so an HC device and id are always provided */
 /* always called with process context; sleeping is OK */
 
index f4a4aeda40b7e3f1876f20dfa7371adeaba70db7..59e20568e8f947381753f590fdebf8b5467267bb 100644 (file)
@@ -21,6 +21,8 @@
 
 #include <linux/device.h>
 #include <linux/signal.h>
+#include <linux/platform_device.h>
+
 #include <asm/mach-types.h>
 #include <asm/hardware.h>
 #include <asm/arch/pxa-regs.h>
index fab420a2ce712b4bcbcbb6ed38a6ba32b0e18f5c..ee1fc605b402c438729871c87c536ee4b2343200 100644 (file)
@@ -19,6 +19,8 @@
  * This file is licenced under the GPL.
 */
 
+#include <linux/platform_device.h>
+
 #include <asm/hardware.h>
 #include <asm/hardware/clock.h>
 #include <asm/arch/usb-control.h>
index b7fd3f644e1e557bb6924987181eced909f28cd7..b1aa350fd32f04d5b59322bfb0ea946c00c25083 100644 (file)
@@ -138,11 +138,23 @@ reset_needed:
 }
 EXPORT_SYMBOL_GPL(uhci_check_and_reset_hc);
 
+static inline int io_type_enabled(struct pci_dev *pdev, unsigned int mask)
+{
+       u16 cmd;
+       return !pci_read_config_word(pdev, PCI_COMMAND, &cmd) && (cmd & mask);
+}
+
+#define pio_enabled(dev) io_type_enabled(dev, PCI_COMMAND_IO)
+#define mmio_enabled(dev) io_type_enabled(dev, PCI_COMMAND_MEMORY)
+
 static void __devinit quirk_usb_handoff_uhci(struct pci_dev *pdev)
 {
        unsigned long base = 0;
        int i;
 
+       if (!pio_enabled(pdev))
+               return;
+
        for (i = 0; i < PCI_ROM_RESOURCE; i++)
                if ((pci_resource_flags(pdev, i) & IORESOURCE_IO)) {
                        base = pci_resource_start(pdev, i);
@@ -153,12 +165,20 @@ static void __devinit quirk_usb_handoff_uhci(struct pci_dev *pdev)
                uhci_check_and_reset_hc(pdev, base);
 }
 
+static int __devinit mmio_resource_enabled(struct pci_dev *pdev, int idx)
+{
+       return pci_resource_start(pdev, idx) && mmio_enabled(pdev);
+}
+
 static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev)
 {
        void __iomem *base;
        int wait_time;
        u32 control;
 
+       if (!mmio_resource_enabled(pdev, 0))
+               return;
+
        base = ioremap_nocache(pci_resource_start(pdev, 0),
                                     pci_resource_len(pdev, 0));
        if (base == NULL) return;
@@ -201,6 +221,9 @@ static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev)
        u32 hcc_params, val, temp;
        u8 cap_length;
 
+       if (!mmio_resource_enabled(pdev, 0))
+               return;
+
        base = ioremap_nocache(pci_resource_start(pdev, 0),
                                pci_resource_len(pdev, 0));
        if (base == NULL) return;
index 40169d9cf2b1855c6b5bdd060e16291423af8aad..5607c0ae683569525358ed5eabeec8c47781eb6d 100644 (file)
@@ -54,6 +54,7 @@
 #include <linux/interrupt.h>
 #include <linux/usb.h>
 #include <linux/usb_sl811.h>
+#include <linux/platform_device.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
index 38aebe361ca140cb8e0255a474a0c5c87144dd8c..e73faf831b24bce4c1a0a16a9b017cdaae274f16 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/string.h>
 #include <linux/timer.h>
 #include <linux/ioport.h>
+#include <linux/platform_device.h>
 
 #include <pcmcia/cs_types.h>
 #include <pcmcia/cs.h>
index f02965f39501666fa9edc28b61a24b47fe2e3561..9b6a39348f81ab4415f01d8837c0990219b88efb 100644 (file)
@@ -26,7 +26,7 @@
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/fb.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 
 #include <asm/hardware.h>
index d28457e0c06373c34dfea3dfea8ec916b86bc489..126daff1c848fafba595223580ffbd847322118a 100644 (file)
@@ -47,6 +47,7 @@
 #include <linux/fb.h>
 #include <linux/init.h>
 #include <linux/arcfb.h>
+#include <linux/platform_device.h>
 
 #include <asm/uaccess.h>
 
index 1991fdb32dfbc7b0d2592f2337c7265755d89d32..4867498f68e8e0b073157e472ce033e725251b60 100644 (file)
@@ -14,7 +14,7 @@
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/spinlock.h>
 #include <linux/fb.h>
 #include <linux/backlight.h>
index 1dbb82dca40b915649a288bec2f36f9cd8bd9999..1785686a7f11cc8a6120323a6106bc327eb6f345 100644 (file)
@@ -6,6 +6,8 @@
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
 #include <asm/setup.h>
 #include <asm/system.h>
 #include <asm/irq.h>
index 116e808d71cd1819512e197af5aed5efd9b1346c..7363d0b25fdfc12fda931d845e24321e346a82b4 100644 (file)
@@ -54,6 +54,8 @@
 #include <linux/fb.h>
 #include <linux/init.h>
 #include <linux/ioport.h>
+#include <linux/platform_device.h>
+
 #include <asm/types.h>
 #include <asm/io.h>
 #include <asm/uaccess.h>
index 485604cd446268661ee82aa2bfc6d44bad0d8ae9..316bfe994811f54deadf88da8371b4da2bfbce8d 100644 (file)
@@ -11,7 +11,7 @@
 
 #include <linux/config.h>
 #include <linux/delay.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 #include <linux/errno.h>
 #include <linux/fb.h>
index 0b9301facbd356885f2ec2c8727a427358638b37..64d9bcc38da387fee71f375a0cfde75ce2d9bc4b 100644 (file)
@@ -31,7 +31,7 @@
 #include <linux/init.h>
 #include <linux/ioport.h>
 #include <linux/cpufreq.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 
 #include <asm/hardware.h>
index 6206da9dd5dad23a61c8b901e4bb0e70c7c0c36a..efd9333b05c24001b13a8212be03a91215dbcdaf 100644 (file)
@@ -36,7 +36,7 @@
 #include <linux/init.h>
 #include <linux/ioport.h>
 #include <linux/cpufreq.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 
 #include <asm/hardware.h>
index 162012bb9264edd2bb58af8b4d9b609e4b940697..8416b2e2b501f7cc723f95f55525f21196a429bd 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
+#include <linux/platform_device.h>
 
 #include <asm/uaccess.h>
 #include <asm/setup.h>
index cb2f7a1de947795f3910dbac1794f65d2e496cb8..f4437430dc5f1bf1633ebd1e00f2d152ce31f452 100644 (file)
@@ -30,7 +30,7 @@
 
 #include <linux/config.h>
 #include <linux/module.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/delay.h>
 
 #include <linux/types.h>
index 3862d3cb1fb2d9b1511ef94a80d2b9b72e8c08d8..3cef90456a4b3fb341c10defa60043aad77404b8 100644 (file)
@@ -86,6 +86,7 @@
 #include <linux/interrupt.h>
 #include <linux/workqueue.h>
 #include <linux/wait.h>
+#include <linux/platform_device.h>
 
 #include <asm/io.h>
 #include <asm/uaccess.h>
index 78e5f194b0df3969daba87721acb30b2b370ec28..3d35b28aaac7140affeedd874c4e9e3093b82204 100644 (file)
 #include <linux/init.h>
 #include <linux/ioport.h>
 #include <linux/cpufreq.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 
 #include <asm/hardware.h>
index 8413907b379a147c1ac112604d7f897ed64a43ce..cf5106eab2d583ee387a9496bae2bc2373739f62 100644 (file)
@@ -18,6 +18,8 @@
 #include <linux/fb.h>
 #include <linux/init.h>
 #include <linux/ioport.h>
+#include <linux/platform_device.h>
+
 #include <asm/io.h>
 #include <asm/mtrr.h>
 
index b1243da55fc5b9c53e96a1d3a6ddbfc802926e2e..3cc23106641db10e1a7787f52c5f9c3ee6d4dcf1 100644 (file)
@@ -19,6 +19,8 @@
 #include <linux/fb.h>
 #include <linux/ioport.h>
 #include <linux/init.h>
+#include <linux/platform_device.h>
+
 #include <video/vga.h>
 #include <asm/io.h>
 #include <asm/mtrr.h>
index b137a3fe07525afed563ff99075adc57deb1d1b2..92d46555dd86e2fd02ee7985ef8ba41ca44d1b48 100644 (file)
@@ -20,6 +20,8 @@
 #include <linux/vmalloc.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
 #include <asm/uaccess.h>
 #include <linux/fb.h>
 #include <linux/init.h>
index 752bf88906a9de13b6578c8c0f2d989af87e795a..cf8cdb108fd95c0d1eb2f3be7e947804e6a5abb8 100644 (file)
@@ -25,7 +25,7 @@
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/string.h>
 #include <linux/vmalloc.h>
 #include <asm/io.h>
index 72fdc10dfdd7c94327ad87efff1dec2adbb67cbd..8848e4dfa026d1eb07be1784d4961a48d62c36ac 100644 (file)
@@ -32,6 +32,10 @@ Domen Puncer
 Jesper Juhl (in particular for lots of whitespace/formatting cleanup)
 Vince Negri and Dave Stahl (for finding an important caching bug)
 Adrian Bunk (kcalloc cleanups)
+Miklos Szeredi 
+Kazeon team for various fixes especially for 2.4 version.
+Asser Ferno (Change Notify support)
+Shaggy (Dave Kleikamp) for inumerable small fs suggestions and some good cleanup
 
 Test case and Bug Report contributors
 -------------------------------------
index 3196d4c4eed36fde53bc857563b2dff2d403abb9..5bab24f59053f0960c9aba5e8529c17618a6eea8 100644 (file)
@@ -1,8 +1,52 @@
+Version 1.39
+------------
+Defer close of a file handle slightly if pending writes depend on that file handle
+(this reduces the EBADF bad file handle errors that can be logged under heavy
+stress on writes).
+
+Version 1.38
+------------
+Fix tcp socket retransmission timeouts (e.g. on ENOSPACE from the socket)
+to be smaller at first (but increasing) so large write performance performance
+over GigE is better.  Do not hang thread on illegal byte range lock response
+from Windows (Windows can send an RFC1001 size which does not match smb size) by
+allowing an SMBs TCP length to be up to a few bytes longer than it should be.
+wsize and rsize can now be larger than negotiated buffer size if server
+supports large readx/writex, even when directio mount flag not specified.
+Write size will in many cases now be 16K instead of 4K which greatly helps
+file copy performance on lightly loaded networks.  Fix oops in dnotify
+when experimental config flag enabled. Make cifsFYI more granular.
+
+Version 1.37
+------------
+Fix readdir caching when unlink removes file in current search buffer,
+and this is followed by a rewind search to just before the deleted entry.
+Do not attempt to set ctime unless atime and/or mtime change requested
+(most servers throw it away anyway). Fix length check of received smbs
+to be more accurate. Fix big endian problem with mapchars mount option,
+and with a field returned by statfs.
+
+Version 1.36
+------------
+Add support for mounting to older pre-CIFS servers such as Windows9x and ME.
+For these older servers, add option for passing netbios name of server in
+on mount (servernetbiosname).  Add suspend support for power management, to
+avoid cifsd thread preventing software suspend from working.
+Add mount option for disabling the default behavior of sending byte range lock
+requests to the server (necessary for certain applications which break with
+mandatory lock behavior such as Evolution), and also mount option for
+requesting case insensitive matching for path based requests (requesting
+case sensitive is the default).
+
 Version 1.35
 ------------
 Add writepage performance improvements.  Fix path name conversions
 for long filenames on mounts which were done with "mapchars" mount option
-specified.
+specified.  Ensure multiplex ids do not collide.  Fix case in which 
+rmmod can oops if done soon after last unmount.  Fix truncated
+search (readdir) output when resume filename was a long filename.
+Fix filename conversion when mapchars mount option was specified and
+filename was a long filename.
 
 Version 1.34
 ------------
@@ -11,7 +55,7 @@ Do not oops if root user kills cifs oplock kernel thread or
 kills the cifsd thread (NB: killing the cifs kernel threads is not
 recommended, unmount and rmmod cifs will kill them when they are
 no longer needed).  Fix readdir to ASCII servers (ie older servers
-which do not support Unicode) and also require asterik.
+which do not support Unicode) and also require asterisk.
 Fix out of memory case in which data could be written one page
 off in the page cache.
 
@@ -101,7 +145,7 @@ improperly zeroed buffer in CIFS Unix extensions set times call.
 
 Version 1.25
 ------------
-Fix internationlization problem in cifs readdir with filenames that map to 
+Fix internationalization problem in cifs readdir with filenames that map to 
 longer UTF8 strings than the string on the wire was in Unicode.  Add workaround
 for readdir to netapp servers. Fix search rewind (seek into readdir to return 
 non-consecutive entries).  Do not do readdir when server negotiates 
@@ -276,7 +320,7 @@ Fix caching problem when files opened by multiple clients in which
 page cache could contain stale data, and write through did
 not occur often enough while file was still open when read ahead
 (read oplock) not allowed.  Treat "sep=" when first mount option
-as an overrride of comma as the default separator between mount
+as an override of comma as the default separator between mount
 options. 
 
 Version 1.01
@@ -286,7 +330,7 @@ Allow passwords longer than 16 bytes. Allow null password string.
 Version 1.00
 ------------
 Gracefully clean up failed mounts when attempting to mount to servers such as
-Windows 98 that terminate tcp sessions during prototocol negotiation.  Handle
+Windows 98 that terminate tcp sessions during protocol negotiation.  Handle
 embedded commas in mount parsing of passwords.
 
 Version 0.99
@@ -295,7 +339,7 @@ Invalidate local inode cached pages on oplock break and when last file
 instance is closed so that the client does not continue using stale local
 copy rather than later modified server copy of file.  Do not reconnect
 when server drops the tcp session prematurely before negotiate
-protocol response.  Fix oops in roepen_file when dentry freed.  Allow
+protocol response.  Fix oops in reopen_file when dentry freed.  Allow
 the support for CIFS Unix Extensions to be disabled via proc interface.
 
 Version 0.98
@@ -637,7 +681,7 @@ versions of 2.4 kernel (now builds and works again on kernels at least as early
 Version 0.41
 ------------
 Various minor fixes for Connectathon Posix "basic" file i/o test suite.  Directory caching fixed so hardlinked
-files now return the correct rumber of links on fstat as they are repeatedly linked and unlinked.
+files now return the correct number of links on fstat as they are repeatedly linked and unlinked.
 
 Version 0.40
 ------------
@@ -704,7 +748,7 @@ session)
 and cleaned them up and made them more consistent with other cifs functions. 
 
 7) Server support for Unix extensions is now fully detected and FindFirst is implemented both ways 
-(with or without Unix exentions) but FindNext and QueryPathInfo with the Unix extensions are not completed,
+(with or without Unix extensions) but FindNext and QueryPathInfo with the Unix extensions are not completed,
 nor is the symlink support using the Unix extensions
 
 8) Started adding the readlink and follow_link code 
index 34b0cf7111f384aff0ea2afc9504b95fd67cd564..bb90941826adb4e60d662d930aa293055c42cd2b 100644 (file)
@@ -294,8 +294,10 @@ A partial list of the supported mount options follows:
                during the local client kernel build will be used.
                If server does not support Unicode, this parameter is
                unused.
-  rsize                default read size
-  wsize                default write size
+  rsize                default read size (usually 16K)
+  wsize                default write size (usually 16K, 32K is often better over GigE)
+               maximum wsize currently allowed by CIFS is 57344 (14 4096 byte
+               pages)
   rw           mount the network share read-write (note that the
                server may still consider the share read-only)
   ro           mount network share read-only
@@ -407,6 +409,13 @@ A partial list of the supported mount options follows:
                This has no effect if the server does not support
                Unicode on the wire.
  nomapchars     Do not translate any of these seven characters (default).
+ nocase         Request case insensitive path name matching (case
+               sensitive is the default if the server suports it).
+ nobrl          Do not send byte range lock requests to the server.
+               This is necessary for certain applications that break
+               with cifs style mandatory byte range locks (and most
+               cifs servers do not yet support requesting advisory
+               byte range locks).
  remount        remount the share (often used to change from ro to rw mounts
                or vice versa)
                
@@ -473,9 +482,16 @@ These experimental features and tracing can be enabled by changing flags in
 kernel, e.g.  insmod cifs).  To enable a feature set it to 1 e.g.  to enable 
 tracing to the kernel message log type: 
 
-       echo 1 > /proc/fs/cifs/cifsFYI
+       echo 7 > /proc/fs/cifs/cifsFYI
        
-and for more extensive tracing including the start of smb requests and responses
+cifsFYI functions as a bit mask. Setting it to 1 enables additional kernel
+logging of various informational messages.  2 enables logging of non-zero
+SMB return codes while 4 enables logging of requests that take longer
+than one second to complete (except for byte range lock requests). 
+Setting it to 4 requires defining CONFIG_CIFS_STATS2 manually in the
+source code (typically by setting it in the beginning of cifsglob.h),
+and setting it to seven enables all three.  Finally, tracing
+the start of smb requests and responses can be enabled via:
 
        echo 1 > /proc/fs/cifs/traceSMB
 
index 8cc881694e2911d0a2eff3ca7eb5dd5efc0503d3..c909298d11ed60deba5809a8e3346130019e63bf 100644 (file)
@@ -1,4 +1,4 @@
-version 1.34 April 29, 2005
+version 1.37 October 9, 2005
 
 A Partial List of Missing Features
 ==================================
@@ -7,14 +7,14 @@ Contributions are welcome.  There are plenty of opportunities
 for visible, important contributions to this module.  Here
 is a partial list of the known problems and missing features:
 
-a) Support for SecurityDescriptors for chmod/chgrp/chown so
-these can be supported for Windows servers
+a) Support for SecurityDescriptors(Windows/CIFS ACLs) for chmod/chgrp/chown
+so that these operations can be supported to Windows servers
 
-b) Better pam/winbind integration (e.g. to handle uid mapping
-better)
+b) Mapping POSIX ACLs (and eventually NFSv4 ACLs) to CIFS
+SecurityDescriptors
 
-c) multi-user mounts - multiplexed sessionsetups over single vc
-(ie tcp session) - more testing needed
+c) Better pam/winbind integration (e.g. to handle uid mapping
+better)
 
 d) Kerberos/SPNEGO session setup support - (started)
 
@@ -29,12 +29,17 @@ f) Directory entry caching relies on a 1 second timer, rather than
 using FindNotify or equivalent.  - (started)
 
 g) A few byte range testcases fail due to POSIX vs. Windows/CIFS
-style byte range lock differences
+style byte range lock differences.  Save byte range locks so
+reconnect can replay them.  
 
-h) quota support
+h) Support unlock all (unlock 0,MAX_OFFSET)
+by unlocking all known byte range locks that we locked on the file.
 
-j) finish writepages support (multi-page write behind for improved
-performance) and syncpage
+i) quota support (needs minor kernel change since quota calls
+to make it to network filesystems or deviceless filesystems)
+
+j) investigate sync behavior (including syncpage) and check  
+for proper behavior of intr/nointr
 
 k) hook lower into the sockets api (as NFS/SunRPC does) to avoid the
 extra copy in/out of the socket buffers in some cases.
@@ -57,20 +62,18 @@ p) Add support for storing symlink and fifo info to Windows servers
 in the Extended Attribute format their SFU clients would recognize.
 
 q) Finish fcntl D_NOTIFY support so kde and gnome file list windows
-will autorefresh (started)
+will autorefresh (partially complete by Asser). Needs minor kernel
+vfs change to support removing D_NOTIFY on a file.   
 
 r) Add GUI tool to configure /proc/fs/cifs settings and for display of
 the CIFS statistics (started)
 
-q) implement support for security and trusted categories of xattrs
+s) implement support for security and trusted categories of xattrs
 (requires minor protocol extension) to enable better support for SELINUX
 
-r) Implement O_DIRECT flag on open (already supported on mount)
-
-s) Allow remapping of last remaining character (\) to +0xF000 which
-(this character is valid for POSIX but not for Windows)
+t) Implement O_DIRECT flag on open (already supported on mount)
 
-t) Create UID mapping facility so server UIDs can be mapped on a per
+u) Create UID mapping facility so server UIDs can be mapped on a per
 mount or a per server basis to client UIDs or nobody if no mapping
 exists.  This is helpful when Unix extensions are negotiated to
 allow better permission checking when UIDs differ on the server
@@ -78,6 +81,17 @@ and client.  Add new protocol request to the CIFS protocol
 standard for asking the server for the corresponding name of a
 particular uid.
 
+v) Add support for CIFS Unix and also the newer POSIX extensions to the
+server side for Samba 4.
+
+w) Finish up the dos time conversion routines needed to return old server
+time to the client (default time, of now or time 0 is used now for these 
+very old servers)
+
+x) Add support for OS/2 (LANMAN 1.2 and LANMAN2.1 based SMB servers)
+
+y) Finish testing of Windows 9x/Windows ME server support (started).
+
 KNOWN BUGS (updated April 29, 2005)
 ====================================
 See http://bugzilla.samba.org - search on product "CifsVFS" for
index e02010dd73ec589a95d1543566519999c970bf2e..98539e2afe81e92494ad27c4c49789126847df8e 100644 (file)
@@ -191,7 +191,8 @@ asn1_header_decode(struct asn1_ctx *ctx,
                   unsigned char **eoc,
                   unsigned int *cls, unsigned int *con, unsigned int *tag)
 {
-       unsigned int def, len;
+       unsigned int def = 0; 
+       unsigned int len = 0;
 
        if (!asn1_id_decode(ctx, cls, con, tag))
                return 0;
index 4061e43471c1a1dc1822fcae3dca02c4406794f0..22a444a3fe4c2fb9325a0fdaa2e4eb09d3707670 100644 (file)
@@ -81,6 +81,8 @@ cifs_debug_data_read(char *buf, char **beginBuffer, off_t offset,
        buf += length;
        length = sprintf(buf,"CIFS Version %s\n",CIFS_VERSION);
        buf += length;
+       length = sprintf(buf,"Active VFS Requests: %d\n", GlobalTotalActiveXid);
+       buf += length;
        length = sprintf(buf, "Servers:");
        buf += length;
 
@@ -97,7 +99,7 @@ cifs_debug_data_read(char *buf, char **beginBuffer, off_t offset,
                } else {
                        length =
                            sprintf(buf,
-                                   "\n%d) Name: %s  Domain: %s Mounts: %d ServerOS: %s  \n\tServerNOS: %s\tCapabilities: 0x%x\n\tSMB session status: %d\t",
+                                   "\n%d) Name: %s  Domain: %s Mounts: %d OS: %s  \n\tNOS: %s\tCapability: 0x%x\n\tSMB session status: %d\t",
                                i, ses->serverName, ses->serverDomain,
                                atomic_read(&ses->inUse),
                                ses->serverOS, ses->serverNOS,
@@ -105,12 +107,18 @@ cifs_debug_data_read(char *buf, char **beginBuffer, off_t offset,
                        buf += length;
                }
                if(ses->server) {
-                       buf += sprintf(buf, "TCP status: %d\n\tLocal Users To Server: %d SecMode: 0x%x Req Active: %d",
+                       buf += sprintf(buf, "TCP status: %d\n\tLocal Users To Server: %d SecMode: 0x%x Req On Wire: %d",
                                ses->server->tcpStatus,
                                atomic_read(&ses->server->socketUseCount),
                                ses->server->secMode,
                                atomic_read(&ses->server->inFlight));
-                       
+
+#ifdef CONFIG_CIFS_STATS2
+                       buf += sprintf(buf, " In Send: %d In MaxReq Wait: %d",
+                               atomic_read(&ses->server->inSend), 
+                               atomic_read(&ses->server->num_waiters));
+#endif
+
                        length = sprintf(buf, "\nMIDs:\n");
                        buf += length;
 
@@ -149,7 +157,7 @@ cifs_debug_data_read(char *buf, char **beginBuffer, off_t offset,
                dev_type = le32_to_cpu(tcon->fsDevInfo.DeviceType);
                length =
                    sprintf(buf,
-                           "\n%d) %s Uses: %d Type: %s Characteristics: 0x%x Attributes: 0x%x\nPathComponentMax: %d Status: %d",
+                           "\n%d) %s Uses: %d Type: %s DevInfo: 0x%x Attributes: 0x%x\nPathComponentMax: %d Status: %d",
                            i, tcon->treeName,
                            atomic_read(&tcon->useCount),
                            tcon->nativeFileSystem,
@@ -195,6 +203,49 @@ cifs_debug_data_read(char *buf, char **beginBuffer, off_t offset,
 }
 
 #ifdef CONFIG_CIFS_STATS
+
+static int
+cifs_stats_write(struct file *file, const char __user *buffer,
+               unsigned long count, void *data)
+{
+        char c;
+        int rc;
+       struct list_head *tmp;
+       struct cifsTconInfo *tcon;
+
+        rc = get_user(c, buffer);
+        if (rc)
+                return rc;
+
+        if (c == '1' || c == 'y' || c == 'Y' || c == '0') {
+               read_lock(&GlobalSMBSeslock);
+               list_for_each(tmp, &GlobalTreeConnectionList) {
+                       tcon = list_entry(tmp, struct cifsTconInfo,
+                                       cifsConnectionList);
+                       atomic_set(&tcon->num_smbs_sent, 0);
+                       atomic_set(&tcon->num_writes, 0);
+                       atomic_set(&tcon->num_reads, 0);
+                       atomic_set(&tcon->num_oplock_brks, 0);
+                       atomic_set(&tcon->num_opens, 0);
+                       atomic_set(&tcon->num_closes, 0);
+                       atomic_set(&tcon->num_deletes, 0);
+                       atomic_set(&tcon->num_mkdirs, 0);
+                       atomic_set(&tcon->num_rmdirs, 0);
+                       atomic_set(&tcon->num_renames, 0);
+                       atomic_set(&tcon->num_t2renames, 0);
+                       atomic_set(&tcon->num_ffirst, 0);
+                       atomic_set(&tcon->num_fnext, 0);
+                       atomic_set(&tcon->num_fclose, 0);
+                       atomic_set(&tcon->num_hardlinks, 0);
+                       atomic_set(&tcon->num_symlinks, 0);
+                       atomic_set(&tcon->num_locks, 0);
+               }
+               read_unlock(&GlobalSMBSeslock);
+       }
+
+        return count;
+}
+
 static int
 cifs_stats_read(char *buf, char **beginBuffer, off_t offset,
                  int count, int *eof, void *data)
@@ -254,35 +305,51 @@ cifs_stats_read(char *buf, char **beginBuffer, off_t offset,
                        buf += sprintf(buf, "\tDISCONNECTED ");
                        length += 14;
                }
-               item_length = sprintf(buf,"\nSMBs: %d Oplock Breaks: %d",
+               item_length = sprintf(buf, "\nSMBs: %d Oplock Breaks: %d",
                        atomic_read(&tcon->num_smbs_sent),
                        atomic_read(&tcon->num_oplock_brks));
                buf += item_length;
                length += item_length;
-               item_length = sprintf(buf,"\nReads: %d Bytes %lld",
+               item_length = sprintf(buf, "\nReads:  %d Bytes: %lld",
                        atomic_read(&tcon->num_reads),
                        (long long)(tcon->bytes_read));
                buf += item_length;
                length += item_length;
-               item_length = sprintf(buf,"\nWrites: %d Bytes: %lld",
+               item_length = sprintf(buf, "\nWrites: %d Bytes: %lld",
                        atomic_read(&tcon->num_writes),
                        (long long)(tcon->bytes_written));
+                buf += item_length;
+                length += item_length;
+                item_length = sprintf(buf, 
+                       "\nLocks: %d HardLinks: %d Symlinks: %d",
+                        atomic_read(&tcon->num_locks),
+                       atomic_read(&tcon->num_hardlinks),
+                       atomic_read(&tcon->num_symlinks));
+                buf += item_length;
+                length += item_length;
+
+               item_length = sprintf(buf, "\nOpens: %d Closes: %d Deletes: %d",
+                       atomic_read(&tcon->num_opens),
+                       atomic_read(&tcon->num_closes),
+                       atomic_read(&tcon->num_deletes));
                buf += item_length;
                length += item_length;
-               item_length = sprintf(buf,
-                       "\nOpens: %d Deletes: %d\nMkdirs: %d Rmdirs: %d",
-                       atomic_read(&tcon->num_opens),
-                       atomic_read(&tcon->num_deletes),
+               item_length = sprintf(buf, "\nMkdirs: %d Rmdirs: %d",
                        atomic_read(&tcon->num_mkdirs),
                        atomic_read(&tcon->num_rmdirs));
                buf += item_length;
                length += item_length;
-               item_length = sprintf(buf,
-                       "\nRenames: %d T2 Renames %d",
+               item_length = sprintf(buf, "\nRenames: %d T2 Renames %d",
                        atomic_read(&tcon->num_renames),
                        atomic_read(&tcon->num_t2renames));
                buf += item_length;
                length += item_length;
+               item_length = sprintf(buf, "\nFindFirst: %d FNext %d FClose %d",
+                       atomic_read(&tcon->num_ffirst),
+                       atomic_read(&tcon->num_fnext),
+                       atomic_read(&tcon->num_fclose));
+               buf += item_length;
+               length += item_length;
        }
        read_unlock(&GlobalSMBSeslock);
 
@@ -341,8 +408,10 @@ cifs_proc_init(void)
                                cifs_debug_data_read, NULL);
 
 #ifdef CONFIG_CIFS_STATS
-       create_proc_read_entry("Stats", 0, proc_fs_cifs,
+       pde = create_proc_read_entry("Stats", 0, proc_fs_cifs,
                                cifs_stats_read, NULL);
+       if (pde)
+               pde->write_proc = cifs_stats_write;
 #endif
        pde = create_proc_read_entry("cifsFYI", 0, proc_fs_cifs,
                                cifsFYI_read, NULL);
@@ -360,7 +429,7 @@ cifs_proc_init(void)
        if (pde)
                pde->write_proc = oplockEnabled_write;
 
-       pde = create_proc_read_entry("ReenableOldCifsReaddirCode", 0, proc_fs_cifs,
+       pde = create_proc_read_entry("Experimental", 0, proc_fs_cifs,
                                quotaEnabled_read, NULL);
        if (pde)
                pde->write_proc = quotaEnabled_write;
@@ -419,7 +488,7 @@ cifs_proc_clean(void)
        remove_proc_entry("ExtendedSecurity",proc_fs_cifs);
        remove_proc_entry("PacketSigningEnabled",proc_fs_cifs);
        remove_proc_entry("LinuxExtensionsEnabled",proc_fs_cifs);
-       remove_proc_entry("ReenableOldCifsReaddirCode",proc_fs_cifs);
+       remove_proc_entry("Experimental",proc_fs_cifs);
        remove_proc_entry("LookupCacheEnabled",proc_fs_cifs);
        remove_proc_entry("cifs", proc_root_fs);
 }
@@ -459,6 +528,8 @@ cifsFYI_write(struct file *file, const char __user *buffer,
                cifsFYI = 0;
        else if (c == '1' || c == 'y' || c == 'Y')
                cifsFYI = 1;
+       else if((c > '1') && (c <= '9'))
+               cifsFYI = (int) (c - '0'); /* see cifs_debug.h for meanings */
 
        return count;
 }
index bf24d2828f685764ce19b66bb05736560c9719c4..4304d9dcfb6c6501853b533f7f3d320fcbcd95bf 100644 (file)
@@ -26,6 +26,9 @@
 void cifs_dump_mem(char *label, void *data, int length);
 extern int traceSMB;           /* flag which enables the function below */
 void dump_smb(struct smb_hdr *, int);
+#define CIFS_INFO      0x01
+#define CIFS_RC        0x02
+#define CIFS_TIMER     0x04
 
 /*
  *     debug ON
@@ -36,7 +39,7 @@ void dump_smb(struct smb_hdr *, int);
 
 /* information message: e.g., configuration, major event */
 extern int cifsFYI;
-#define cifsfyi(format,arg...) if (cifsFYI) printk(KERN_DEBUG " " __FILE__ ": " format "\n" "" , ## arg)
+#define cifsfyi(format,arg...) if (cifsFYI & CIFS_INFO) printk(KERN_DEBUG " " __FILE__ ": " format "\n" "" , ## arg)
 
 #define cFYI(button,prspec) if (button) cifsfyi prspec
 
index ec00d61d53080b1b5e771ba3b0d0d2a83ce6e30a..f799f6f0e7296927af5bfc7203d2c5b489441de5 100644 (file)
@@ -24,6 +24,9 @@
 #define CIFS_MOUNT_DIRECT_IO    8 /* do not write nor read through page cache */
 #define CIFS_MOUNT_NO_XATTR  0x10 /* if set - disable xattr support */
 #define CIFS_MOUNT_MAP_SPECIAL_CHR 0x20 /* remap illegal chars in filenames */
+#define CIFS_MOUNT_POSIX_PATHS 0x40 /* Negotiate posix pathnames if possible. */
+#define CIFS_MOUNT_UNX_EMUL    0x80 /* Network compat with SFUnix emulation */
+#define CIFS_MOUNT_NO_BRL      0x100 /* No sending byte range locks to srv */
 
 struct cifs_sb_info {
        struct cifsTconInfo *tcon;      /* primary mount */
index 1ebf7dafc1d757128e4084d6a1794485399b7ecc..877095a1192ad8e13b7bffe6e041ab578346ec46 100644 (file)
@@ -59,6 +59,8 @@ unsigned int ntlmv2_support = 0;
 unsigned int sign_CIFS_PDUs = 1;
 extern struct task_struct * oplockThread; /* remove sparse warning */
 struct task_struct * oplockThread = NULL;
+extern struct task_struct * dnotifyThread; /* remove sparse warning */
+struct task_struct * dnotifyThread = NULL;
 unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE;
 module_param(CIFSMaxBufSize, int, 0);
 MODULE_PARM_DESC(CIFSMaxBufSize,"Network buffer size (not including header). Default: 16384 Range: 8192 to 130048");
@@ -73,6 +75,7 @@ module_param(cifs_max_pending, int, 0);
 MODULE_PARM_DESC(cifs_max_pending,"Simultaneous requests to server. Default: 50 Range: 2 to 256");
 
 static DECLARE_COMPLETION(cifs_oplock_exited);
+static DECLARE_COMPLETION(cifs_dnotify_exited);
 
 extern mempool_t *cifs_sm_req_poolp;
 extern mempool_t *cifs_req_poolp;
@@ -202,6 +205,10 @@ cifs_statfs(struct super_block *sb, struct kstatfs *buf)
 #endif /* CIFS_EXPERIMENTAL */
        rc = CIFSSMBQFSInfo(xid, pTcon, buf);
 
+       /* Old Windows servers do not support level 103, retry with level 
+          one if old server failed the previous call */ 
+       if(rc)
+               rc = SMBOldQFSInfo(xid, pTcon, buf);
        /*     
           int f_type;
           __fsid_t f_fsid;
@@ -253,7 +260,7 @@ cifs_alloc_inode(struct super_block *sb)
        cifs_inode->clientCanCacheAll = FALSE;
        cifs_inode->vfs_inode.i_blksize = CIFS_MAX_MSGSIZE;
        cifs_inode->vfs_inode.i_blkbits = 14;  /* 2**14 = CIFS_MAX_MSGSIZE */
-
+       cifs_inode->vfs_inode.i_flags = S_NOATIME | S_NOCMTIME;
        INIT_LIST_HEAD(&cifs_inode->openFileList);
        return &cifs_inode->vfs_inode;
 }
@@ -398,6 +405,34 @@ static struct quotactl_ops cifs_quotactl_ops = {
 };
 #endif
 
+static void cifs_umount_begin(struct super_block * sblock)
+{
+       struct cifs_sb_info *cifs_sb;
+       struct cifsTconInfo * tcon;
+
+       cifs_sb = CIFS_SB(sblock);
+       if(cifs_sb == NULL)
+               return;
+
+       tcon = cifs_sb->tcon;
+       if(tcon == NULL)
+               return;
+       down(&tcon->tconSem);
+       if (atomic_read(&tcon->useCount) == 1)
+               tcon->tidStatus = CifsExiting;
+       up(&tcon->tconSem);
+
+       if(tcon->ses && tcon->ses->server)
+       {
+               cERROR(1,("wake up tasks now - umount begin not complete"));
+               wake_up_all(&tcon->ses->server->request_q);
+       }
+/* BB FIXME - finish add checks for tidStatus BB */
+
+       return;
+}
+       
+
 static int cifs_remount(struct super_block *sb, int *flags, char *data)
 {
        *flags |= MS_NODIRATIME;
@@ -415,7 +450,7 @@ struct super_operations cifs_super_ops = {
    unless later we add lazy close of inodes or unless the kernel forgets to call
    us with the same number of releases (closes) as opens */
        .show_options = cifs_show_options,
-/*    .umount_begin   = cifs_umount_begin, *//* consider adding in the future */
+/*     .umount_begin   = cifs_umount_begin, */ /* BB finish in the future */
        .remount_fs = cifs_remount,
 };
 
@@ -783,9 +818,7 @@ static int cifs_oplock_thread(void * dummyarg)
        do {
                if (try_to_freeze()) 
                        continue;
-               set_current_state(TASK_INTERRUPTIBLE);
                
-               schedule_timeout(1*HZ);  
                spin_lock(&GlobalMid_Lock);
                if(list_empty(&GlobalOplock_Q)) {
                        spin_unlock(&GlobalMid_Lock);
@@ -834,10 +867,27 @@ static int cifs_oplock_thread(void * dummyarg)
                                }
                        } else
                                spin_unlock(&GlobalMid_Lock);
+                       set_current_state(TASK_INTERRUPTIBLE);
+                       schedule_timeout(1);  /* yield in case q were corrupt */
                }
        } while(!signal_pending(current));
-       complete_and_exit (&cifs_oplock_exited, 0);
        oplockThread = NULL;
+       complete_and_exit (&cifs_oplock_exited, 0);
+}
+
+static int cifs_dnotify_thread(void * dummyarg)
+{
+       daemonize("cifsdnotifyd");
+       allow_signal(SIGTERM);
+
+       dnotifyThread = current;
+       do {
+               if(try_to_freeze())
+                       continue;
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(39*HZ);
+       } while(!signal_pending(current));
+       complete_and_exit (&cifs_dnotify_exited, 0);
 }
 
 static int __init
@@ -851,6 +901,10 @@ init_cifs(void)
        INIT_LIST_HEAD(&GlobalSMBSessionList);
        INIT_LIST_HEAD(&GlobalTreeConnectionList);
        INIT_LIST_HEAD(&GlobalOplock_Q);
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+       INIT_LIST_HEAD(&GlobalDnotifyReqList);
+       INIT_LIST_HEAD(&GlobalDnotifyRsp_Q);
+#endif 
 /*
  *  Initialize Global counters
  */
@@ -886,10 +940,16 @@ init_cifs(void)
                                if (!rc) {                
                                        rc = (int)kernel_thread(cifs_oplock_thread, NULL, 
                                                CLONE_FS | CLONE_FILES | CLONE_VM);
-                                       if(rc > 0)
-                                               return 0;
-                                       else 
+                                       if(rc > 0) {
+                                               rc = (int)kernel_thread(cifs_dnotify_thread, NULL,
+                                                       CLONE_FS | CLONE_FILES | CLONE_VM);
+                                               if(rc > 0)
+                                                       return 0;
+                                               else
+                                                       cERROR(1,("error %d create dnotify thread", rc));
+                                       } else {
                                                cERROR(1,("error %d create oplock thread",rc));
+                                       }
                                }
                                cifs_destroy_request_bufs();
                        }
@@ -918,6 +978,10 @@ exit_cifs(void)
                send_sig(SIGTERM, oplockThread, 1);
                wait_for_completion(&cifs_oplock_exited);
        }
+       if(dnotifyThread) {
+               send_sig(SIGTERM, dnotifyThread, 1);
+               wait_for_completion(&cifs_dnotify_exited);
+       }
 }
 
 MODULE_AUTHOR("Steve French <sfrench@us.ibm.com>");
index 1fd21f66f2435198e33eca7a2919ef42b08ef6ce..1223fa81dbd269fc696e90951787e29426273f64 100644 (file)
@@ -81,6 +81,7 @@ extern int cifs_dir_notify(struct file *, unsigned long arg);
 
 /* Functions related to dir entries */
 extern struct dentry_operations cifs_dentry_ops;
+extern struct dentry_operations cifs_ci_dentry_ops;
 
 /* Functions related to symlinks */
 extern void *cifs_follow_link(struct dentry *direntry, struct nameidata *nd);
@@ -96,5 +97,5 @@ extern ssize_t        cifs_getxattr(struct dentry *, const char *, void *, size_t);
 extern ssize_t cifs_listxattr(struct dentry *, char *, size_t);
 extern int cifs_ioctl (struct inode * inode, struct file * filep,
                       unsigned int command, unsigned long arg);
-#define CIFS_VERSION   "1.35"
+#define CIFS_VERSION   "1.39"
 #endif                         /* _CIFSFS_H */
index 81babab265e1a31fcad6c97429fadc8d052890fd..1ba08f8c5bc4aafac85cf3f83929391d6c633e17 100644 (file)
@@ -110,8 +110,9 @@ enum protocolEnum {
  */
 
 struct TCP_Server_Info {
-       char server_Name[SERVER_NAME_LEN_WITH_NULL];    /* 15 chars + X'20'in 16th */
-       char unicode_server_Name[SERVER_NAME_LEN_WITH_NULL * 2];        /* Unicode version of server_Name */
+       /* 15 character server name + 0x20 16th byte indicating type = srv */
+       char server_RFC1001_name[SERVER_NAME_LEN_WITH_NULL];
+       char unicode_server_Name[SERVER_NAME_LEN_WITH_NULL * 2];
        struct socket *ssocket;
        union {
                struct sockaddr_in sockAddr;
@@ -122,13 +123,17 @@ struct TCP_Server_Info {
        struct list_head pending_mid_q;
        void *Server_NlsInfo;   /* BB - placeholder for future NLS info  */
        unsigned short server_codepage; /* codepage for the server    */
-       unsigned long ip_address;       /* IP addr for the server if known     */
+       unsigned long ip_address;       /* IP addr for the server if known */
        enum protocolEnum protocolType; 
        char versionMajor;
        char versionMinor;
        unsigned svlocal:1;     /* local server or remote */
        atomic_t socketUseCount; /* number of open cifs sessions on socket */
        atomic_t inFlight;  /* number of requests on the wire to server */
+#ifdef CONFIG_CIFS_STATS2
+       atomic_t inSend; /* requests trying to send */
+       atomic_t num_waiters;   /* blocked waiting to get in sendrecv */
+#endif
        enum statusEnum tcpStatus; /* what we think the status is */
        struct semaphore tcpSem;
        struct task_struct *tsk;
@@ -147,8 +152,10 @@ struct TCP_Server_Info {
        /* (returned on Negotiate */
        int capabilities; /* allow selective disabling of caps by smb sess */
        __u16 timeZone;
+       __u16 CurrentMid;         /* multiplex id - rotating counter */
        char cryptKey[CIFS_CRYPTO_KEY_SIZE];
-       char workstation_RFC1001_name[16]; /* 16th byte is always zero */
+       /* 16th byte of RFC1001 workstation name is always null */
+       char workstation_RFC1001_name[SERVER_NAME_LEN_WITH_NULL];
        __u32 sequence_number; /* needed for CIFS PDU signature */
        char mac_signing_key[CIFS_SESSION_KEY_SIZE + 16]; 
 };
@@ -214,19 +221,41 @@ struct cifsTconInfo {
        atomic_t num_reads;
        atomic_t num_oplock_brks;
        atomic_t num_opens;
+       atomic_t num_closes;
        atomic_t num_deletes;
        atomic_t num_mkdirs;
        atomic_t num_rmdirs;
        atomic_t num_renames;
        atomic_t num_t2renames;
+       atomic_t num_ffirst;
+       atomic_t num_fnext;
+       atomic_t num_fclose;
+       atomic_t num_hardlinks;
+       atomic_t num_symlinks;
+       atomic_t num_locks;
+#ifdef CONFIG_CIFS_STATS2
+       unsigned long long time_writes;
+       unsigned long long time_reads;
+       unsigned long long time_opens;
+       unsigned long long time_deletes;
+       unsigned long long time_closes;
+       unsigned long long time_mkdirs;
+       unsigned long long time_rmdirs;
+       unsigned long long time_renames;
+       unsigned long long time_t2renames;
+       unsigned long long time_ffirst;
+       unsigned long long time_fnext;
+       unsigned long long time_fclose;
+#endif /* CONFIG_CIFS_STATS2 */
        __u64    bytes_read;
        __u64    bytes_written;
        spinlock_t stat_lock;
-#endif
+#endif /* CONFIG_CIFS_STATS */
        FILE_SYSTEM_DEVICE_INFO fsDevInfo;
        FILE_SYSTEM_ATTRIBUTE_INFO fsAttrInfo;  /* ok if file system name truncated */
        FILE_SYSTEM_UNIX_INFO fsUnixInfo;
        unsigned retry:1;
+       unsigned nocase:1;
        /* BB add field for back pointer to sb struct? */
 };
 
@@ -270,6 +299,7 @@ struct cifsFileInfo {
        struct inode * pInode; /* needed for oplock break */
        unsigned closePend:1;   /* file is marked to close */
        unsigned invalidHandle:1;  /* file closed via session abend */
+       atomic_t wrtPending;   /* handle in use - defer close */
        struct semaphore fh_sem; /* prevents reopen race after dead ses*/
        char * search_resume_name; /* BB removeme BB */
        unsigned int resume_name_length; /* BB removeme - field renamed and moved BB */
@@ -306,6 +336,41 @@ CIFS_SB(struct super_block *sb)
        return sb->s_fs_info;
 }
 
+static inline char CIFS_DIR_SEP(const struct cifs_sb_info *cifs_sb)
+{
+       if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)
+               return '/';
+       else
+               return '\\';
+}
+
+#ifdef CONFIG_CIFS_STATS
+#define cifs_stats_inc atomic_inc
+
+static inline void cifs_stats_bytes_written(struct cifsTconInfo *tcon,
+                                           unsigned int bytes)
+{
+       if (bytes) {
+               spin_lock(&tcon->stat_lock);
+               tcon->bytes_written += bytes;
+               spin_unlock(&tcon->stat_lock);
+       }
+}
+
+static inline void cifs_stats_bytes_read(struct cifsTconInfo *tcon,
+                                        unsigned int bytes)
+{
+       spin_lock(&tcon->stat_lock);
+       tcon->bytes_read += bytes;
+       spin_unlock(&tcon->stat_lock);
+}
+#else
+
+#define  cifs_stats_inc(field) do {} while(0)
+#define  cifs_stats_bytes_written(tcon, bytes) do {} while(0)
+#define  cifs_stats_bytes_read(tcon, bytes) do {} while(0)
+
+#endif
 
 /* one of these for every pending CIFS request to the server */
 struct mid_q_entry {
@@ -313,7 +378,11 @@ struct mid_q_entry {
        __u16 mid;              /* multiplex id */
        __u16 pid;              /* process id */
        __u32 sequence_number;  /* for CIFS signing */
-       struct timeval when_sent;       /* time when smb sent */
+       unsigned long when_alloc;  /* when mid was created */
+#ifdef CONFIG_CIFS_STATS2
+       unsigned long when_sent; /* time when smb send finished */
+       unsigned long when_received; /* when demux complete (taken off wire) */
+#endif
        struct cifsSesInfo *ses;        /* smb was sent to this server */
        struct task_struct *tsk;        /* task waiting for response */
        struct smb_hdr *resp_buf;       /* response buffer */
@@ -331,6 +400,20 @@ struct oplock_q_entry {
        __u16 netfid;
 };
 
+/* for pending dnotify requests */
+struct dir_notify_req {
+       struct list_head lhead;
+       __le16 Pid;
+       __le16 PidHigh;
+       __u16 Mid;
+       __u16 Tid;
+       __u16 Uid;
+       __u16 netfid;
+       __u32 filter; /* CompletionFilter (for multishot) */
+       int multishot;
+       struct file * pfile;
+};
+
 #define   MID_FREE 0
 #define   MID_REQUEST_ALLOCATED 1
 #define   MID_REQUEST_SUBMITTED 2
@@ -399,6 +482,9 @@ GLOBAL_EXTERN rwlock_t GlobalSMBSeslock;  /* protects list inserts on 3 above */
 
 GLOBAL_EXTERN struct list_head GlobalOplock_Q;
 
+GLOBAL_EXTERN struct list_head GlobalDnotifyReqList; /* Outstanding dir notify requests */
+GLOBAL_EXTERN struct list_head GlobalDnotifyRsp_Q; /* Dir notify response queue */
+
 /*
  * Global transaction id (XID) information
  */
index aede6a81316794a1a20de497c9684f96e20f6f0c..48a05b9df7eb5f6ef8d66e8b252ee534dbfd257f 100644 (file)
 #define SMB_COM_CLOSE                 0x04 /* triv req/rsp, timestamp ignored */
 #define SMB_COM_DELETE                0x06 /* trivial response */
 #define SMB_COM_RENAME                0x07 /* trivial response */
+#define SMB_COM_QUERY_INFORMATION     0x08 /* aka getattr */
 #define SMB_COM_SETATTR               0x09 /* trivial response */
 #define SMB_COM_LOCKING_ANDX          0x24 /* trivial response */
 #define SMB_COM_COPY                  0x29 /* trivial rsp, fail filename ignrd*/
+#define SMB_COM_OPEN_ANDX             0x2D /* Legacy open for old servers */
 #define SMB_COM_READ_ANDX             0x2E
 #define SMB_COM_WRITE_ANDX            0x2F
 #define SMB_COM_TRANSACTION2          0x32
@@ -52,6 +54,7 @@
 #define SMB_COM_NT_TRANSACT           0xA0
 #define SMB_COM_NT_TRANSACT_SECONDARY 0xA1
 #define SMB_COM_NT_CREATE_ANDX        0xA2
+#define SMB_COM_NT_CANCEL             0xA4 /* no response */
 #define SMB_COM_NT_RENAME             0xA5 /* trivial response */
 
 /* Transact2 subcommand codes */
@@ -59,6 +62,7 @@
 #define TRANS2_FIND_FIRST             0x01
 #define TRANS2_FIND_NEXT              0x02
 #define TRANS2_QUERY_FS_INFORMATION   0x03
+#define TRANS2_SET_FS_INFORMATION     0x04
 #define TRANS2_QUERY_PATH_INFORMATION 0x05
 #define TRANS2_SET_PATH_INFORMATION   0x06
 #define TRANS2_QUERY_FILE_INFORMATION 0x07
@@ -76,7 +80,7 @@
 #define NT_TRANSACT_GET_USER_QUOTA    0x07
 #define NT_TRANSACT_SET_USER_QUOTA    0x08
 
-#define MAX_CIFS_HDR_SIZE 256  /* chained NTCreateXReadX will probably be biggest */
+#define MAX_CIFS_HDR_SIZE 256  /* is future chained NTCreateXReadX bigger? */
 
 /* internal cifs vfs structures */
 /*****************************************************************
 /*
  * SMB flag definitions 
  */
-#define SMBFLG_EXTD_LOCK 0x01  /* server supports lock-read write-unlock primitives */
+#define SMBFLG_EXTD_LOCK 0x01  /* server supports lock-read write-unlock smb */
 #define SMBFLG_RCV_POSTED 0x02 /* obsolete */
 #define SMBFLG_RSVD 0x04
-#define SMBFLG_CASELESS 0x08   /* all pathnames treated as caseless (off implies case sensitive file handling requested) */
+#define SMBFLG_CASELESS 0x08   /* all pathnames treated as caseless (off
+                               implies case sensitive file handling request) */
 #define SMBFLG_CANONICAL_PATH_FORMAT 0x10      /* obsolete */
 #define SMBFLG_OLD_OPLOCK 0x20 /* obsolete */
 #define SMBFLG_OLD_OPLOCK_NOTIFY 0x40  /* obsolete */
 /*
  * SMB flag2 definitions 
  */
-#define SMBFLG2_KNOWS_LONG_NAMES cpu_to_le16(1)        /* can send long (non-8.3) path names in response */
+#define SMBFLG2_KNOWS_LONG_NAMES cpu_to_le16(1)        /* can send long (non-8.3) 
+                                                  path names in response */
 #define SMBFLG2_KNOWS_EAS cpu_to_le16(2)
 #define SMBFLG2_SECURITY_SIGNATURE cpu_to_le16(4)
 #define SMBFLG2_IS_LONG_NAME cpu_to_le16(0x40)
  * file and can have any suitable combination of the following values:
  */
 
-#define FILE_READ_DATA        0x00000001       /* Data can be read from the file   */
-#define FILE_WRITE_DATA       0x00000002       /* Data can be written to the file  */
-#define FILE_APPEND_DATA      0x00000004       /* Data can be appended to the file */
-#define FILE_READ_EA          0x00000008       /* Extended attributes associated   */
-                                        /* with the file can be read        */
-#define FILE_WRITE_EA         0x00000010       /* Extended attributes associated   */
-                                        /* with the file can be written     */
-#define FILE_EXECUTE          0x00000020       /*Data can be read into memory from */
-                                        /* the file using system paging I/O */
+#define FILE_READ_DATA        0x00000001  /* Data can be read from the file   */
+#define FILE_WRITE_DATA       0x00000002  /* Data can be written to the file  */
+#define FILE_APPEND_DATA      0x00000004  /* Data can be appended to the file */
+#define FILE_READ_EA          0x00000008  /* Extended attributes associated   */
+                                         /* with the file can be read        */
+#define FILE_WRITE_EA         0x00000010  /* Extended attributes associated   */
+                                         /* with the file can be written     */
+#define FILE_EXECUTE          0x00000020  /*Data can be read into memory from */
+                                         /* the file using system paging I/O */
 #define FILE_DELETE_CHILD     0x00000040
-#define FILE_READ_ATTRIBUTES  0x00000080       /* Attributes associated with the   */
-                                        /* file can be read                 */
-#define FILE_WRITE_ATTRIBUTES 0x00000100       /* Attributes associated with the   */
-                                        /* file can be written              */
-#define DELETE                0x00010000       /* The file can be deleted          */
-#define READ_CONTROL          0x00020000       /* The access control list and      */
-                                        /* ownership associated with the    */
-                                        /* file can be read                 */
-#define WRITE_DAC             0x00040000       /* The access control list and      */
-                                        /* ownership associated with the    */
-                                        /* file can be written.             */
-#define WRITE_OWNER           0x00080000       /* Ownership information associated */
-                                        /* with the file can be written     */
-#define SYNCHRONIZE           0x00100000       /* The file handle can waited on to */
-                                        /* synchronize with the completion  */
-                                        /* of an input/output request       */
+#define FILE_READ_ATTRIBUTES  0x00000080  /* Attributes associated with the   */
+                                         /* file can be read                 */
+#define FILE_WRITE_ATTRIBUTES 0x00000100  /* Attributes associated with the   */
+                                         /* file can be written              */
+#define DELETE                0x00010000  /* The file can be deleted          */
+#define READ_CONTROL          0x00020000  /* The access control list and      */
+                                         /* ownership associated with the    */
+                                         /* file can be read                 */
+#define WRITE_DAC             0x00040000  /* The access control list and      */
+                                         /* ownership associated with the    */
+                                         /* file can be written.             */
+#define WRITE_OWNER           0x00080000  /* Ownership information associated */
+                                         /* with the file can be written     */
+#define SYNCHRONIZE           0x00100000  /* The file handle can waited on to */
+                                         /* synchronize with the completion  */
+                                         /* of an input/output request       */
 #define GENERIC_ALL           0x10000000
 #define GENERIC_EXECUTE       0x20000000
 #define GENERIC_WRITE         0x40000000
                                         /* In summary - Relevant file       */
                                         /* access flags from CIFS are       */
                                         /* file_read_data, file_write_data  */
-                                        /* file_execute, file_read_attributes */
+                                        /* file_execute, file_read_attributes*/
                                         /* write_dac, and delete.           */
 
 /*
 #define ATTR_SPARSE    0x0200
 #define ATTR_REPARSE   0x0400
 #define ATTR_COMPRESSED 0x0800
-#define ATTR_OFFLINE    0x1000 /* ie file not immediately available - offline storage */
+#define ATTR_OFFLINE    0x1000 /* ie file not immediately available - 
+                                       on offline storage */
 #define ATTR_NOT_CONTENT_INDEXED 0x2000
 #define ATTR_ENCRYPTED  0x4000
 #define ATTR_POSIX_SEMANTICS 0x01000000
 /* CreateOptions */
 #define CREATE_NOT_FILE                0x00000001      /* if set must not be file */
 #define CREATE_WRITE_THROUGH   0x00000002
-#define CREATE_NOT_DIR         0x00000040      /* if set must not be directory */
+#define CREATE_SEQUENTIAL       0x00000004
+#define CREATE_SYNC_ALERT       0x00000010
+#define CREATE_ASYNC_ALERT      0x00000020
+#define CREATE_NOT_DIR         0x00000040    /* if set must not be directory */
+#define CREATE_NO_EA_KNOWLEDGE  0x00000200
+#define CREATE_EIGHT_DOT_THREE  0x00000400
 #define CREATE_RANDOM_ACCESS   0x00000800
 #define CREATE_DELETE_ON_CLOSE 0x00001000
+#define CREATE_OPEN_BY_ID       0x00002000
 #define OPEN_REPARSE_POINT     0x00200000
+#define CREATE_OPTIONS_MASK     0x007FFFFF 
+#define CREATE_OPTION_SPECIAL   0x20000000   /* system. NB not sent over wire */
 
 /* ImpersonationLevel flags */
 #define SECURITY_ANONYMOUS      0
 #define GETU16(var)  (*((__u16 *)var)) /* BB check for endian issues */
 #define GETU32(var)  (*((__u32 *)var)) /* BB check for endian issues */
 
-#pragma pack(1)
-
 struct smb_hdr {
-       __u32 smb_buf_length;   /* big endian on wire *//* BB length is only two or three bytes - with one or two byte type preceding it but that is always zero - we could mask the type byte off just in case BB */
+       __u32 smb_buf_length;   /* big endian on wire *//* BB length is only two
+               or three bytes - with one or two byte type preceding it that are
+               zero - we could mask the type byte off just in case BB */
        __u8 Protocol[4];
        __u8 Command;
        union {
@@ -308,9 +323,9 @@ struct smb_hdr {
                        __u8 ErrorClass;
                        __u8 Reserved;
                        __le16 Error;
-               } DosError;
+               } __attribute__((packed)) DosError;
                __le32 CifsError;
-       } Status;
+       } __attribute__((packed)) Status;
        __u8 Flags;
        __le16 Flags2;          /* note: le */
        __le16 PidHigh;
@@ -318,16 +333,16 @@ struct smb_hdr {
                struct {
                        __le32 SequenceNumber;  /* le */
                        __u32 Reserved; /* zero */
-               } Sequence;
+               } __attribute__((packed)) Sequence;
                __u8 SecuritySignature[8];      /* le */
-       } Signature;
+       } __attribute__((packed)) Signature;
        __u8 pad[2];
        __u16 Tid;
        __le16 Pid;
        __u16 Uid;
        __u16 Mid;
        __u8 WordCount;
-};
+} __attribute__((packed));
 /* given a pointer to an smb_hdr retrieve the value of byte count */
 #define BCC(smb_var) ( *(__u16 *)((char *)smb_var + sizeof(struct smb_hdr) + (2* smb_var->WordCount) ) )
 #define BCC_LE(smb_var) ( *(__le16 *)((char *)smb_var + sizeof(struct smb_hdr) + (2* smb_var->WordCount) ) )
@@ -379,7 +394,7 @@ typedef struct negotiate_req {
        struct smb_hdr hdr;     /* wct = 0 */
        __le16 ByteCount;
        unsigned char DialectsArray[1];
-} NEGOTIATE_REQ;
+} __attribute__((packed)) NEGOTIATE_REQ;
 
 typedef struct negotiate_rsp {
        struct smb_hdr hdr;     /* wct = 17 */
@@ -397,16 +412,16 @@ typedef struct negotiate_rsp {
        __u8 EncryptionKeyLength;
        __u16 ByteCount;
        union {
-               unsigned char EncryptionKey[1]; /* if cap extended security is off */
+               unsigned char EncryptionKey[1]; /* cap extended security off */
                /* followed by Domain name - if extended security is off */
                /* followed by 16 bytes of server GUID */
-               /* followed by security blob if cap_extended_security negotiated */
+               /* then security blob if cap_extended_security negotiated */
                struct {
                        unsigned char GUID[16];
                        unsigned char SecurityBlob[1];
-               } extended_response;
-       } u;
-} NEGOTIATE_RSP;
+               } __attribute__((packed)) extended_response;
+       } __attribute__((packed)) u;
+} __attribute__((packed)) NEGOTIATE_RSP;
 
 /* SecurityMode bits */
 #define SECMODE_USER          0x01     /* off indicates share level security */
@@ -452,7 +467,8 @@ typedef union smb_com_session_setup_andx {
                unsigned char SecurityBlob[1];  /* followed by */
                /* STRING NativeOS */
                /* STRING NativeLanMan */
-       } req;                  /* NTLM request format (with extended security */
+       } __attribute__((packed)) req;  /* NTLM request format (with 
+                                       extended security */
 
        struct {                /* request format */
                struct smb_hdr hdr;     /* wct = 13 */
@@ -463,18 +479,19 @@ typedef union smb_com_session_setup_andx {
                __le16 MaxMpxCount;
                __le16 VcNumber;
                __u32 SessionKey;
-               __le16 CaseInsensitivePasswordLength;   /* ASCII password length */
-               __le16 CaseSensitivePasswordLength;     /* Unicode password length */
+               __le16 CaseInsensitivePasswordLength; /* ASCII password len */
+               __le16 CaseSensitivePasswordLength; /* Unicode password length*/
                __u32 Reserved; /* see below */
                __le32 Capabilities;
                __le16 ByteCount;
-               unsigned char CaseInsensitivePassword[1];       /* followed by: */
+               unsigned char CaseInsensitivePassword[1];     /* followed by: */
                /* unsigned char * CaseSensitivePassword; */
                /* STRING AccountName */
                /* STRING PrimaryDomain */
                /* STRING NativeOS */
                /* STRING NativeLanMan */
-       } req_no_secext;        /* NTLM request format (without extended security */
+       } __attribute__((packed)) req_no_secext; /* NTLM request format (without
+                                                       extended security */
 
        struct {                /* default (NTLM) response format */
                struct smb_hdr hdr;     /* wct = 4 */
@@ -488,7 +505,7 @@ typedef union smb_com_session_setup_andx {
 /*      unsigned char  * NativeOS;      */
 /*     unsigned char  * NativeLanMan;  */
 /*      unsigned char  * PrimaryDomain; */
-       } resp;                 /* NTLM response format (with or without extended security */
+       } __attribute__((packed)) resp;                 /* NTLM response format (with or without extended security */
 
        struct {                /* request format */
                struct smb_hdr hdr;     /* wct = 10 */
@@ -507,7 +524,7 @@ typedef union smb_com_session_setup_andx {
                /* STRING PrimaryDomain */
                /* STRING NativeOS */
                /* STRING NativeLanMan */
-       } old_req;              /* pre-NTLM (LANMAN2.1) request format */
+       } __attribute__((packed)) old_req;              /* pre-NTLM (LANMAN2.1) request format */
 
        struct {                /* default (NTLM) response format */
                struct smb_hdr hdr;     /* wct = 3 */
@@ -519,8 +536,8 @@ typedef union smb_com_session_setup_andx {
                unsigned char NativeOS[1];      /* followed by */
 /*     unsigned char * NativeLanMan; */
 /*      unsigned char * PrimaryDomain; */
-       } old_resp;             /* pre-NTLM (LANMAN2.1) response format */
-} SESSION_SETUP_ANDX;
+       } __attribute__((packed)) old_resp;             /* pre-NTLM (LANMAN2.1) response format */
+} __attribute__((packed)) SESSION_SETUP_ANDX;
 
 #define CIFS_NETWORK_OPSYS "CIFS VFS Client for Linux"
 
@@ -530,7 +547,8 @@ typedef union smb_com_session_setup_andx {
 #define CAP_NT_SMBS            0x00000010
 #define CAP_STATUS32           0x00000040
 #define CAP_LEVEL_II_OPLOCKS   0x00000080
-#define CAP_NT_FIND            0x00000200      /* reserved should be zero (presumably because NT_SMBs implies the same thing) */
+#define CAP_NT_FIND            0x00000200      /* reserved should be zero 
+                               (because NT_SMBs implies the same thing?) */
 #define CAP_BULK_TRANSFER      0x20000000
 #define CAP_EXTENDED_SECURITY  0x80000000
 
@@ -548,7 +566,7 @@ typedef struct smb_com_tconx_req {
        unsigned char Password[1];      /* followed by */
 /* STRING Path    *//* \\server\share name */
        /* STRING Service */
-} TCONX_REQ;
+} __attribute__((packed)) TCONX_REQ;
 
 typedef struct smb_com_tconx_rsp {
        struct smb_hdr hdr;     /* wct = 3 *//* note that Win2000 has sent wct=7 in some cases on responses. Four unspecified words followed OptionalSupport */
@@ -559,13 +577,14 @@ typedef struct smb_com_tconx_rsp {
        __u16 ByteCount;
        unsigned char Service[1];       /* always ASCII, not Unicode */
        /* STRING NativeFileSystem */
-} TCONX_RSP;
+} __attribute__((packed)) TCONX_RSP;
 
 /* tree connect Flags */
 #define DISCONNECT_TID          0x0001
 #define TCON_EXTENDED_SECINFO   0x0008
 /* OptionalSupport bits */
-#define SMB_SUPPORT_SEARCH_BITS 0x0001 /* must have bits (exclusive searches suppt. */
+#define SMB_SUPPORT_SEARCH_BITS 0x0001 /* "must have" directory search bits
+                                        (exclusive searches supported) */
 #define SMB_SHARE_IS_IN_DFS     0x0002
 
 typedef struct smb_com_logoff_andx_req {
@@ -574,7 +593,7 @@ typedef struct smb_com_logoff_andx_req {
        __u8 AndXReserved;
        __u16 AndXOffset;
        __u16 ByteCount;
-} LOGOFF_ANDX_REQ;
+} __attribute__((packed)) LOGOFF_ANDX_REQ;
 
 typedef struct smb_com_logoff_andx_rsp {
        struct smb_hdr hdr;     /* wct = 2 */
@@ -582,38 +601,39 @@ typedef struct smb_com_logoff_andx_rsp {
        __u8 AndXReserved;
        __u16 AndXOffset;
        __u16 ByteCount;
-} LOGOFF_ANDX_RSP;
+} __attribute__((packed)) LOGOFF_ANDX_RSP;
 
 typedef union smb_com_tree_disconnect {        /* as an altetnative can use flag on tree_connect PDU to effect disconnect *//* probably the simplest SMB PDU */
        struct {
                struct smb_hdr hdr;     /* wct = 0 */
                __u16 ByteCount;        /* bcc = 0 */
-       } req;
+       } __attribute__((packed)) req;
        struct {
                struct smb_hdr hdr;     /* wct = 0 */
                __u16 ByteCount;        /* bcc = 0 */
-       } resp;
-} TREE_DISCONNECT;
+       } __attribute__((packed)) resp;
+} __attribute__((packed)) TREE_DISCONNECT;
 
 typedef struct smb_com_close_req {
        struct smb_hdr hdr;     /* wct = 3 */
        __u16 FileID;
        __u32 LastWriteTime;    /* should be zero */
        __u16 ByteCount;        /* 0 */
-} CLOSE_REQ;
+} __attribute__((packed)) CLOSE_REQ;
 
 typedef struct smb_com_close_rsp {
        struct smb_hdr hdr;     /* wct = 0 */
        __u16 ByteCount;        /* bct = 0 */
-} CLOSE_RSP;
+} __attribute__((packed)) CLOSE_RSP;
 
 typedef struct smb_com_findclose_req {
        struct smb_hdr hdr; /* wct = 1 */
        __u16 FileID;
        __u16 ByteCount;    /* 0 */
-} FINDCLOSE_REQ;
+} __attribute__((packed)) FINDCLOSE_REQ;
 
 /* OpenFlags */
+#define REQ_MORE_INFO      0x00000001  /* legacy (OPEN_AND_X) only */
 #define REQ_OPLOCK         0x00000002
 #define REQ_BATCHOPLOCK    0x00000004
 #define REQ_OPENDIRONLY    0x00000008
@@ -637,7 +657,7 @@ typedef struct smb_com_open_req {   /* also handles create */
        __u8 SecurityFlags;
        __le16 ByteCount;
        char fileName[1];
-} OPEN_REQ;
+} __attribute__((packed)) OPEN_REQ;
 
 /* open response: oplock levels */
 #define OPLOCK_NONE     0
@@ -667,7 +687,63 @@ typedef struct smb_com_open_rsp {
        __le16 DeviceState;
        __u8 DirectoryFlag;
        __u16 ByteCount;        /* bct = 0 */
-} OPEN_RSP;
+} __attribute__((packed)) OPEN_RSP;
+
+/* format of legacy open request */
+typedef struct smb_com_openx_req {
+       struct smb_hdr  hdr;    /* wct = 15 */
+       __u8 AndXCommand;
+       __u8 AndXReserved;
+       __le16 AndXOffset;
+       __le16 OpenFlags;
+       __le16 Mode;
+       __le16 Sattr; /* search attributes */
+       __le16 FileAttributes;  /* dos attrs */
+       __le32 CreateTime; /* os2 format */
+       __le16 OpenFunction;
+       __le32 EndOfFile;
+       __le32 Timeout;
+       __le32 Reserved;
+       __le16  ByteCount;  /* file name follows */
+       char   fileName[1];
+} __attribute__((packed)) OPENX_REQ;
+
+typedef struct smb_com_openx_rsp {
+       struct smb_hdr  hdr;    /* wct = 15 */
+       __u8 AndXCommand;
+       __u8 AndXReserved;
+       __le16 AndXOffset;
+       __u16  Fid;
+       __le16 FileAttributes;
+       __le32 LastWriteTime; /* os2 format */
+       __le32 EndOfFile;
+       __le16 Access;
+       __le16 FileType;
+       __le16 IPCState;
+       __le16 Action;
+       __u32  FileId;
+       __u16  Reserved;
+       __u16  ByteCount;
+} __attribute__((packed)) OPENX_RSP; 
+
+/* Legacy write request for older servers */
+typedef struct smb_com_writex_req {
+        struct smb_hdr hdr;     /* wct = 12 */
+        __u8 AndXCommand;
+        __u8 AndXReserved;
+        __le16 AndXOffset;
+        __u16 Fid;
+        __le32 OffsetLow;
+        __u32 Reserved; /* Timeout */
+        __le16 WriteMode; /* 1 = write through */
+        __le16 Remaining;
+        __le16 Reserved2;
+        __le16 DataLengthLow;
+        __le16 DataOffset;
+        __le16 ByteCount;
+        __u8 Pad;               /* BB check for whether padded to DWORD boundary and optimum performance here */
+        char Data[0];
+} __attribute__((packed)) WRITEX_REQ;
 
 typedef struct smb_com_write_req {
        struct smb_hdr hdr;     /* wct = 14 */
@@ -686,7 +762,7 @@ typedef struct smb_com_write_req {
        __le16 ByteCount;
        __u8 Pad;               /* BB check for whether padded to DWORD boundary and optimum performance here */
        char Data[0];
-} WRITE_REQ;
+} __attribute__((packed)) WRITE_REQ;
 
 typedef struct smb_com_write_rsp {
        struct smb_hdr hdr;     /* wct = 6 */
@@ -698,7 +774,22 @@ typedef struct smb_com_write_rsp {
        __le16 CountHigh;
        __u16  Reserved;
        __u16 ByteCount;
-} WRITE_RSP;
+} __attribute__((packed)) WRITE_RSP;
+
+/* legacy read request for older servers */
+typedef struct smb_com_readx_req {
+        struct smb_hdr hdr;     /* wct = 10 */
+        __u8 AndXCommand;
+        __u8 AndXReserved;
+        __le16 AndXOffset;
+        __u16 Fid;
+        __le32 OffsetLow;
+        __le16 MaxCount;
+        __le16 MinCount;                /* obsolete */
+        __le32 Reserved;
+        __le16 Remaining;
+        __le16 ByteCount;
+} __attribute__((packed)) READX_REQ;
 
 typedef struct smb_com_read_req {
        struct smb_hdr hdr;     /* wct = 12 */
@@ -713,7 +804,7 @@ typedef struct smb_com_read_req {
        __le16 Remaining;
        __le32 OffsetHigh;
        __le16 ByteCount;
-} READ_REQ;
+} __attribute__((packed)) READ_REQ;
 
 typedef struct smb_com_read_rsp {
        struct smb_hdr hdr;     /* wct = 12 */
@@ -730,7 +821,7 @@ typedef struct smb_com_read_rsp {
        __u16 ByteCount;
        __u8 Pad;               /* BB check for whether padded to DWORD boundary and optimum performance here */
        char Data[1];
-} READ_RSP;
+} __attribute__((packed)) READ_RSP;
 
 typedef struct locking_andx_range {
        __le16 Pid;
@@ -739,7 +830,7 @@ typedef struct locking_andx_range {
        __le32 OffsetLow;
        __le32 LengthHigh;
        __le32 LengthLow;
-} LOCKING_ANDX_RANGE;
+} __attribute__((packed)) LOCKING_ANDX_RANGE;
 
 #define LOCKING_ANDX_SHARED_LOCK     0x01
 #define LOCKING_ANDX_OPLOCK_RELEASE  0x02
@@ -760,7 +851,7 @@ typedef struct smb_com_lock_req {
        __le16 NumberOfLocks;
        __le16 ByteCount;
        LOCKING_ANDX_RANGE Locks[1];
-} LOCK_REQ;
+} __attribute__((packed)) LOCK_REQ;
 
 
 typedef struct cifs_posix_lock {
@@ -770,7 +861,7 @@ typedef struct cifs_posix_lock {
        __le64  start;
        __le64  length;
        /* BB what about additional owner info to identify network client */
-} CIFS_POSIX_LOCK;
+} __attribute__((packed)) CIFS_POSIX_LOCK;
 
 typedef struct smb_com_lock_rsp {
        struct smb_hdr hdr;     /* wct = 2 */
@@ -778,7 +869,7 @@ typedef struct smb_com_lock_rsp {
        __u8 AndXReserved;
        __le16 AndXOffset;
        __u16 ByteCount;
-} LOCK_RSP;
+} __attribute__((packed)) LOCK_RSP;
 
 typedef struct smb_com_rename_req {
        struct smb_hdr hdr;     /* wct = 1 */
@@ -788,7 +879,7 @@ typedef struct smb_com_rename_req {
        unsigned char OldFileName[1];
        /* followed by __u8 BufferFormat2 */
        /* followed by NewFileName */
-} RENAME_REQ;
+} __attribute__((packed)) RENAME_REQ;
 
        /* copy request flags */
 #define COPY_MUST_BE_FILE      0x0001
@@ -808,7 +899,7 @@ typedef struct smb_com_copy_req {
        unsigned char OldFileName[1];
        /* followed by __u8 BufferFormat2 */
        /* followed by NewFileName string */
-} COPY_REQ;
+} __attribute__((packed)) COPY_REQ;
 
 typedef struct smb_com_copy_rsp {
        struct smb_hdr hdr;     /* wct = 1 */
@@ -816,7 +907,7 @@ typedef struct smb_com_copy_rsp {
        __u16 ByteCount;    /* may be zero */
        __u8 BufferFormat;  /* 0x04 - only present if errored file follows */
        unsigned char ErrorFileName[1]; /* only present if error in copy */
-} COPY_RSP;
+} __attribute__((packed)) COPY_RSP;
 
 #define CREATE_HARD_LINK               0x103
 #define MOVEFILE_COPY_ALLOWED          0x0002
@@ -832,12 +923,12 @@ typedef struct smb_com_nt_rename_req {    /* A5 - also used for create hardlink */
        unsigned char OldFileName[1];
        /* followed by __u8 BufferFormat2 */
        /* followed by NewFileName */
-} NT_RENAME_REQ;
+} __attribute__((packed)) NT_RENAME_REQ;
 
 typedef struct smb_com_rename_rsp {
        struct smb_hdr hdr;     /* wct = 0 */
        __u16 ByteCount;        /* bct = 0 */
-} RENAME_RSP;
+} __attribute__((packed)) RENAME_RSP;
 
 typedef struct smb_com_delete_file_req {
        struct smb_hdr hdr;     /* wct = 1 */
@@ -845,36 +936,52 @@ typedef struct smb_com_delete_file_req {
        __le16 ByteCount;
        __u8 BufferFormat;      /* 4 = ASCII */
        unsigned char fileName[1];
-} DELETE_FILE_REQ;
+} __attribute__((packed)) DELETE_FILE_REQ;
 
 typedef struct smb_com_delete_file_rsp {
        struct smb_hdr hdr;     /* wct = 0 */
        __u16 ByteCount;        /* bct = 0 */
-} DELETE_FILE_RSP;
+} __attribute__((packed)) DELETE_FILE_RSP;
 
 typedef struct smb_com_delete_directory_req {
        struct smb_hdr hdr;     /* wct = 0 */
        __le16 ByteCount;
        __u8 BufferFormat;      /* 4 = ASCII */
        unsigned char DirName[1];
-} DELETE_DIRECTORY_REQ;
+} __attribute__((packed)) DELETE_DIRECTORY_REQ;
 
 typedef struct smb_com_delete_directory_rsp {
        struct smb_hdr hdr;     /* wct = 0 */
        __u16 ByteCount;        /* bct = 0 */
-} DELETE_DIRECTORY_RSP;
+} __attribute__((packed)) DELETE_DIRECTORY_RSP;
 
 typedef struct smb_com_create_directory_req {
        struct smb_hdr hdr;     /* wct = 0 */
        __le16 ByteCount;
        __u8 BufferFormat;      /* 4 = ASCII */
        unsigned char DirName[1];
-} CREATE_DIRECTORY_REQ;
+} __attribute__((packed)) CREATE_DIRECTORY_REQ;
 
 typedef struct smb_com_create_directory_rsp {
        struct smb_hdr hdr;     /* wct = 0 */
        __u16 ByteCount;        /* bct = 0 */
-} CREATE_DIRECTORY_RSP;
+} __attribute__((packed)) CREATE_DIRECTORY_RSP;
+
+typedef struct smb_com_query_information_req {
+       struct smb_hdr hdr;     /* wct = 0 */
+       __le16 ByteCount;       /* 1 + namelen + 1 */
+       __u8 BufferFormat;      /* 4 = ASCII */
+       unsigned char FileName[1];
+} __attribute__((packed)) QUERY_INFORMATION_REQ;
+
+typedef struct smb_com_query_information_rsp {
+       struct smb_hdr hdr;     /* wct = 10 */
+       __le16 attr;
+       __le32  last_write_time;
+       __le32 size;
+       __u16  reserved[5];
+       __le16 ByteCount;       /* bcc = 0 */
+} __attribute__((packed)) QUERY_INFORMATION_RSP;
 
 typedef struct smb_com_setattr_req {
        struct smb_hdr hdr; /* wct = 8 */
@@ -885,12 +992,12 @@ typedef struct smb_com_setattr_req {
        __u16  ByteCount;
        __u8   BufferFormat; /* 4 = ASCII */
        unsigned char fileName[1];
-} SETATTR_REQ;
+} __attribute__((packed)) SETATTR_REQ;
 
 typedef struct smb_com_setattr_rsp {
        struct smb_hdr hdr;     /* wct = 0 */
        __u16 ByteCount;        /* bct = 0 */
-} SETATTR_RSP;
+} __attribute__((packed)) SETATTR_RSP;
 
 /* empty wct response to setattr */
 
@@ -920,7 +1027,7 @@ typedef struct smb_com_transaction_ioctl_req {
        __le16 ByteCount;
        __u8 Pad[3];
        __u8 Data[1];
-} TRANSACT_IOCTL_REQ;
+} __attribute__((packed)) TRANSACT_IOCTL_REQ;
 
 typedef struct smb_com_transaction_ioctl_rsp {
        struct smb_hdr hdr;     /* wct = 19 */
@@ -937,7 +1044,7 @@ typedef struct smb_com_transaction_ioctl_rsp {
        __le16 ReturnedDataLen;
        __u16 ByteCount;
        __u8 Pad[3];
-} TRANSACT_IOCTL_RSP;
+} __attribute__((packed)) TRANSACT_IOCTL_RSP;
 
 typedef struct smb_com_transaction_change_notify_req {
        struct smb_hdr hdr;     /* wct = 23 */
@@ -961,7 +1068,7 @@ typedef struct smb_com_transaction_change_notify_req {
        __le16 ByteCount;
 /* __u8 Pad[3];*/
 /*     __u8 Data[1];*/
-} TRANSACT_CHANGE_NOTIFY_REQ;
+} __attribute__((packed)) TRANSACT_CHANGE_NOTIFY_REQ;
 
 typedef struct smb_com_transaction_change_notify_rsp {
        struct smb_hdr hdr;     /* wct = 18 */
@@ -977,7 +1084,7 @@ typedef struct smb_com_transaction_change_notify_rsp {
        __u8 SetupCount;   /* 0 */
        __u16 ByteCount;
        /* __u8 Pad[3]; */
-} TRANSACT_CHANGE_NOTIFY_RSP;
+} __attribute__((packed)) TRANSACT_CHANGE_NOTIFY_RSP;
 /* Completion Filter flags for Notify */
 #define FILE_NOTIFY_CHANGE_FILE_NAME    0x00000001
 #define FILE_NOTIFY_CHANGE_DIR_NAME     0x00000002
@@ -1008,7 +1115,7 @@ struct file_notify_information {
        __le32 Action;
        __le32 FileNameLength;
        __u8  FileName[0];
-}; 
+} __attribute__((packed))
 
 struct reparse_data {
        __u32   ReparseTag;
@@ -1019,7 +1126,7 @@ struct reparse_data {
        __u16   TargetNameOffset;
        __u16   TargetNameLen;
        char    LinkNamesBuf[1];
-};
+} __attribute__((packed));
 
 struct cifs_quota_data {
        __u32   rsrvd1;  /* 0 */
@@ -1029,7 +1136,7 @@ struct cifs_quota_data {
        __u64   soft_limit;
        __u64   hard_limit;
        char    sid[1];  /* variable size? */
-};
+} __attribute__((packed));
 
 /* quota sub commands */
 #define QUOTA_LIST_CONTINUE        0
@@ -1055,12 +1162,12 @@ struct trans2_req {
        __u8 Reserved3;
        __le16 SubCommand; /* 1st setup word - SetupCount words follow */
        __le16 ByteCount;
-};
+} __attribute__((packed));
 
 struct smb_t2_req {
        struct smb_hdr hdr;
        struct trans2_req t2_req;
-};
+} __attribute__((packed));
 
 struct trans2_resp {
        /* struct smb_hdr hdr precedes. Note wct = 10 + setup count */
@@ -1079,12 +1186,12 @@ struct trans2_resp {
        __u16 ByteCount;
        __u16 Reserved2;*/      
        /* data area follows */
-};
+} __attribute__((packed));
 
 struct smb_t2_rsp {
        struct smb_hdr hdr;
        struct trans2_resp t2_rsp;
-};
+} __attribute__((packed));
 
 /* PathInfo/FileInfo infolevels */
 #define SMB_INFO_STANDARD                   1
@@ -1171,14 +1278,14 @@ typedef struct smb_com_transaction2_qpi_req {
        __le16 InformationLevel;
        __u32 Reserved4;
        char FileName[1];
-} TRANSACTION2_QPI_REQ;
+} __attribute__((packed)) TRANSACTION2_QPI_REQ;
 
 typedef struct smb_com_transaction2_qpi_rsp {
        struct smb_hdr hdr;     /* wct = 10 + SetupCount */
        struct trans2_resp t2;
        __u16 ByteCount;
        __u16 Reserved2;        /* parameter word reserved - present for infolevels > 100 */
-} TRANSACTION2_QPI_RSP;
+} __attribute__((packed)) TRANSACTION2_QPI_RSP;
 
 typedef struct smb_com_transaction2_spi_req {
        struct smb_hdr hdr;     /* wct = 15 */
@@ -1204,21 +1311,21 @@ typedef struct smb_com_transaction2_spi_req {
        __le16 InformationLevel;
        __u32 Reserved4;
        char FileName[1];
-} TRANSACTION2_SPI_REQ;
+} __attribute__((packed)) TRANSACTION2_SPI_REQ;
 
 typedef struct smb_com_transaction2_spi_rsp {
        struct smb_hdr hdr;     /* wct = 10 + SetupCount */
        struct trans2_resp t2;
        __u16 ByteCount;
        __u16 Reserved2;        /* parameter word reserved - present for infolevels > 100 */
-} TRANSACTION2_SPI_RSP;
+} __attribute__((packed)) TRANSACTION2_SPI_RSP;
 
 struct set_file_rename {
        __le32 overwrite;   /* 1 = overwrite dest */
        __u32 root_fid;   /* zero */
        __le32 target_name_len;
        char  target_name[0];  /* Must be unicode */
-};
+} __attribute__((packed));
 
 struct smb_com_transaction2_sfi_req {
        struct smb_hdr hdr;     /* wct = 15 */
@@ -1244,7 +1351,7 @@ struct smb_com_transaction2_sfi_req {
        __u16 Fid;
        __le16 InformationLevel;
        __u16 Reserved4;        
-};
+} __attribute__((packed));
 
 struct smb_com_transaction2_sfi_rsp {
        struct smb_hdr hdr;     /* wct = 10 + SetupCount */
@@ -1252,7 +1359,7 @@ struct smb_com_transaction2_sfi_rsp {
        __u16 ByteCount;
        __u16 Reserved2;        /* parameter word reserved - 
                                        present for infolevels > 100 */
-};
+} __attribute__((packed));
 
 struct smb_t2_qfi_req {
         struct smb_hdr hdr;
@@ -1260,7 +1367,7 @@ struct smb_t2_qfi_req {
        __u8    Pad;
        __u16   Fid;
        __le16  InformationLevel;
-};
+} __attribute__((packed));
 
 struct smb_t2_qfi_rsp {
         struct smb_hdr hdr;     /* wct = 10 + SetupCount */
@@ -1268,7 +1375,7 @@ struct smb_t2_qfi_rsp {
         __u16 ByteCount;
         __u16 Reserved2;        /* parameter word reserved - 
                                        present for infolevels > 100 */
-};
+} __attribute__((packed));
 
 /*
  * Flags on T2 FINDFIRST and FINDNEXT 
@@ -1310,13 +1417,13 @@ typedef struct smb_com_transaction2_ffirst_req {
        __le16 InformationLevel;
        __le32 SearchStorageType;
        char FileName[1];
-} TRANSACTION2_FFIRST_REQ;
+} __attribute__((packed)) TRANSACTION2_FFIRST_REQ;
 
 typedef struct smb_com_transaction2_ffirst_rsp {
        struct smb_hdr hdr;     /* wct = 10 */
        struct trans2_resp t2;
        __u16 ByteCount;
-} TRANSACTION2_FFIRST_RSP;
+} __attribute__((packed)) TRANSACTION2_FFIRST_RSP;
 
 typedef struct smb_com_transaction2_ffirst_rsp_parms {
        __u16 SearchHandle;
@@ -1324,7 +1431,7 @@ typedef struct smb_com_transaction2_ffirst_rsp_parms {
        __le16 EndofSearch;
        __le16 EAErrorOffset;
        __le16 LastNameOffset;
-} T2_FFIRST_RSP_PARMS;
+} __attribute__((packed)) T2_FFIRST_RSP_PARMS;
 
 typedef struct smb_com_transaction2_fnext_req {
        struct smb_hdr hdr;     /* wct = 15 */
@@ -1352,20 +1459,20 @@ typedef struct smb_com_transaction2_fnext_req {
        __u32 ResumeKey;
        __le16 SearchFlags;
        char ResumeFileName[1];
-} TRANSACTION2_FNEXT_REQ;
+} __attribute__((packed)) TRANSACTION2_FNEXT_REQ;
 
 typedef struct smb_com_transaction2_fnext_rsp {
        struct smb_hdr hdr;     /* wct = 10 */
        struct trans2_resp t2;
        __u16 ByteCount;
-} TRANSACTION2_FNEXT_RSP;
+} __attribute__((packed)) TRANSACTION2_FNEXT_RSP;
 
 typedef struct smb_com_transaction2_fnext_rsp_parms {
        __le16 SearchCount;
        __le16 EndofSearch;
        __le16 EAErrorOffset;
        __le16 LastNameOffset;
-} T2_FNEXT_RSP_PARMS;
+} __attribute__((packed)) T2_FNEXT_RSP_PARMS;
 
 /* QFSInfo Levels */
 #define SMB_INFO_ALLOCATION         1
@@ -1402,14 +1509,51 @@ typedef struct smb_com_transaction2_qfsi_req {
        __le16 ByteCount;
        __u8 Pad;
        __le16 InformationLevel;
-} TRANSACTION2_QFSI_REQ;
+} __attribute__((packed)) TRANSACTION2_QFSI_REQ;
 
 typedef struct smb_com_transaction_qfsi_rsp {
        struct smb_hdr hdr;     /* wct = 10 + SetupCount */
        struct trans2_resp t2;
        __u16 ByteCount;
        __u8 Pad;               /* may be three bytes *//* followed by data area */
-} TRANSACTION2_QFSI_RSP;
+} __attribute__((packed)) TRANSACTION2_QFSI_RSP;
+
+
+/* SETFSInfo Levels */
+#define SMB_SET_CIFS_UNIX_INFO    0x200
+typedef struct smb_com_transaction2_setfsi_req {
+       struct smb_hdr hdr;     /* wct = 15 */
+       __le16 TotalParameterCount;
+       __le16 TotalDataCount;
+       __le16 MaxParameterCount;
+       __le16 MaxDataCount;
+       __u8 MaxSetupCount;
+       __u8 Reserved;
+       __le16 Flags;
+       __le32 Timeout;
+       __u16 Reserved2;
+       __le16 ParameterCount;  /* 4 */
+       __le16 ParameterOffset;
+       __le16 DataCount;       /* 12 */
+       __le16 DataOffset;
+       __u8 SetupCount;        /* one */
+       __u8 Reserved3;
+       __le16 SubCommand;      /* TRANS2_SET_FS_INFORMATION */
+       __le16 ByteCount;
+       __u8 Pad;
+       __u16 FileNum;          /* Parameters start. */
+       __le16 InformationLevel;/* Parameters end. */
+       __le16 ClientUnixMajor; /* Data start. */
+       __le16 ClientUnixMinor;
+       __le64 ClientUnixCap;   /* Data end */
+} __attribute__((packed)) TRANSACTION2_SETFSI_REQ;
+
+typedef struct smb_com_transaction2_setfsi_rsp {
+       struct smb_hdr hdr;     /* wct = 10 */
+       struct trans2_resp t2;
+       __u16 ByteCount;
+} __attribute__((packed)) TRANSACTION2_SETFSI_RSP;
+
 
 typedef struct smb_com_transaction2_get_dfs_refer_req {
        struct smb_hdr hdr;     /* wct = 15 */
@@ -1433,7 +1577,7 @@ typedef struct smb_com_transaction2_get_dfs_refer_req {
        __u8 Pad[3];            /* Win2K has sent 0x0F01 (max resp length perhaps?) followed by one byte pad - doesn't seem to matter though */
        __le16 MaxReferralLevel;
        char RequestFileName[1];
-} TRANSACTION2_GET_DFS_REFER_REQ;
+} __attribute__((packed)) TRANSACTION2_GET_DFS_REFER_REQ;
 
 typedef struct dfs_referral_level_3 {
        __le16 VersionNumber;
@@ -1445,7 +1589,7 @@ typedef struct dfs_referral_level_3 {
        __le16 DfsPathOffset;
        __le16 DfsAlternatePathOffset;
        __le16 NetworkAddressOffset;
-} REFERRAL3;
+} __attribute__((packed)) REFERRAL3;
 
 typedef struct smb_com_transaction_get_dfs_refer_rsp {
        struct smb_hdr hdr;     /* wct = 10 */
@@ -1458,7 +1602,7 @@ typedef struct smb_com_transaction_get_dfs_refer_rsp {
        __u16 Pad2;
        REFERRAL3 referrals[1]; /* array of level 3 dfs_referral structures */
        /* followed by the strings pointed to by the referral structures */
-} TRANSACTION2_GET_DFS_REFER_RSP;
+} __attribute__((packed)) TRANSACTION2_GET_DFS_REFER_RSP;
 
 /* DFS Flags */
 #define DFSREF_REFERRAL_SERVER  0x0001
@@ -1512,7 +1656,7 @@ struct serverInfo {
        unsigned char versionMinor;
        unsigned long type;
        unsigned int commentOffset;
-};
+} __attribute__((packed));
 
 /*
  * The following structure is the format of the data returned on a NetShareEnum
@@ -1524,39 +1668,55 @@ struct shareInfo {
        char pad;
        unsigned short type;
        unsigned int commentOffset;
-};
+} __attribute__((packed));
 
 struct aliasInfo {
        char aliasName[9];
        char pad;
        unsigned int commentOffset;
        unsigned char type[2];
-};
+} __attribute__((packed));
 
 struct aliasInfo92 {
        int aliasNameOffset;
        int serverNameOffset;
        int shareNameOffset;
-};
+} __attribute__((packed));
 
 typedef struct {
        __le64 TotalAllocationUnits;
        __le64 FreeAllocationUnits;
        __le32 SectorsPerAllocationUnit;
        __le32 BytesPerSector;
-} FILE_SYSTEM_INFO;            /* size info, level 0x103 */
+} __attribute__((packed)) FILE_SYSTEM_INFO;            /* size info, level 0x103 */
+
+typedef struct {
+       __le32 fsid;
+       __le32 SectorsPerAllocationUnit;
+       __le32 TotalAllocationUnits;
+       __le32 FreeAllocationUnits;
+       __le16  BytesPerSector;
+} __attribute__((packed)) FILE_SYSTEM_ALLOC_INFO;
 
 typedef struct {
        __le16 MajorVersionNumber;
        __le16 MinorVersionNumber;
        __le64 Capability;
-} FILE_SYSTEM_UNIX_INFO;       /* Unix extensions info, level 0x200 */
+} __attribute__((packed)) FILE_SYSTEM_UNIX_INFO;       /* Unix extensions info, level 0x200 */
+
+/* Version numbers for CIFS UNIX major and minor. */
+#define CIFS_UNIX_MAJOR_VERSION 1
+#define CIFS_UNIX_MINOR_VERSION 0
+
 /* Linux/Unix extensions capability flags */
 #define CIFS_UNIX_FCNTL_CAP             0x00000001 /* support for fcntl locks */
 #define CIFS_UNIX_POSIX_ACL_CAP         0x00000002 /* support getfacl/setfacl */
 #define CIFS_UNIX_XATTR_CAP             0x00000004 /* support new namespace   */
 #define CIFS_UNIX_EXTATTR_CAP           0x00000008 /* support chattr/chflag   */
+#define CIFS_UNIX_POSIX_PATHNAMES_CAP   0x00000010 /* Use POSIX pathnames on the wire. */
+
 #define CIFS_POSIX_EXTENSIONS           0x00000010 /* support for new QFSInfo */
+
 typedef struct {
        /* For undefined recommended transfer size return -1 in that field */
        __le32 OptimalTransferSize;  /* bsize on some os, iosize on other os */
@@ -1577,7 +1737,7 @@ typedef struct {
        __le64 FileSysIdentifier;   /* fsid */
        /* NB Namelen comes from FILE_SYSTEM_ATTRIBUTE_INFO call */
        /* NB flags can come from FILE_SYSTEM_DEVICE_INFO call   */
-} FILE_SYSTEM_POSIX_INFO;
+} __attribute__((packed)) FILE_SYSTEM_POSIX_INFO;
 
 /* DeviceType Flags */
 #define FILE_DEVICE_CD_ROM              0x00000002
@@ -1602,14 +1762,14 @@ typedef struct {
 typedef struct {
        __le32 DeviceType;
        __le32 DeviceCharacteristics;
-} FILE_SYSTEM_DEVICE_INFO;     /* device info, level 0x104 */
+} __attribute__((packed)) FILE_SYSTEM_DEVICE_INFO;     /* device info, level 0x104 */
 
 typedef struct {
        __le32 Attributes;
        __le32 MaxPathNameComponentLength;
        __le32 FileSystemNameLen;
        char FileSystemName[52]; /* do not really need to save this - so potentially get only subset of name */
-} FILE_SYSTEM_ATTRIBUTE_INFO;
+} __attribute__((packed)) FILE_SYSTEM_ATTRIBUTE_INFO;
 
 /******************************************************************************/
 /* QueryFileInfo/QueryPathinfo (also for SetPath/SetFile) data buffer formats */
@@ -1636,7 +1796,7 @@ typedef struct { /* data block encoding of response to level 263 QPathInfo */
        __le32 AlignmentRequirement;
        __le32 FileNameLength;
        char FileName[1];
-} FILE_ALL_INFO;               /* level 0x107 QPathInfo */
+} __attribute__((packed)) FILE_ALL_INFO;               /* level 0x107 QPathInfo */
 
 /* defines for enumerating possible values of the Unix type field below */
 #define UNIX_FILE      0
@@ -1660,11 +1820,11 @@ typedef struct {
        __u64 UniqueId;
        __le64 Permissions;
        __le64 Nlinks;
-} FILE_UNIX_BASIC_INFO;                /* level 0x200 QPathInfo */
+} __attribute__((packed)) FILE_UNIX_BASIC_INFO;                /* level 0x200 QPathInfo */
 
 typedef struct {
        char LinkDest[1];
-} FILE_UNIX_LINK_INFO;         /* level 0x201 QPathInfo */
+} __attribute__((packed)) FILE_UNIX_LINK_INFO;         /* level 0x201 QPathInfo */
 
 /* The following three structures are needed only for
        setting time to NT4 and some older servers via
@@ -1673,13 +1833,13 @@ typedef struct {
        __u16 Day:5;
        __u16 Month:4;
        __u16 Year:7;
-} SMB_DATE;
+} __attribute__((packed)) SMB_DATE;
 
 typedef struct {
        __u16 TwoSeconds:5;
        __u16 Minutes:6;
        __u16 Hours:5;
-} SMB_TIME;
+} __attribute__((packed)) SMB_TIME;
 
 typedef struct {
        __le16 CreationDate; /* SMB Date see above */
@@ -1692,7 +1852,7 @@ typedef struct {
        __le32 AllocationSize;
        __le16 Attributes; /* verify not u32 */
        __le32 EASize;
-} FILE_INFO_STANDARD;  /* level 1 SetPath/FileInfo */
+} __attribute__((packed)) FILE_INFO_STANDARD;  /* level 1 SetPath/FileInfo */
 
 typedef struct {
        __le64 CreationTime;
@@ -1701,19 +1861,19 @@ typedef struct {
        __le64 ChangeTime;
        __le32 Attributes;
        __u32 Pad;
-} FILE_BASIC_INFO;             /* size info, level 0x101 */
+} __attribute__((packed)) FILE_BASIC_INFO;             /* size info, level 0x101 */
 
 struct file_allocation_info {
        __le64 AllocationSize; /* Note old Samba srvr rounds this up too much */
-};     /* size used on disk, level 0x103 for set, 0x105 for query */
+} __attribute__((packed));     /* size used on disk, level 0x103 for set, 0x105 for query */
 
 struct file_end_of_file_info {
        __le64 FileSize;                /* offset to end of file */
-};     /* size info, level 0x104 for set, 0x106 for query */
+} __attribute__((packed));     /* size info, level 0x104 for set, 0x106 for query */
 
 struct file_alt_name_info {
        __u8   alt_name[1];
-};      /* level 0x0108 */
+} __attribute__((packed));      /* level 0x0108 */
 
 struct file_stream_info {
        __le32 number_of_streams;  /* BB check sizes and verify location */
@@ -1730,7 +1890,7 @@ struct file_compression_info {
        __u8   ch_shift;
        __u8   cl_shift;
        __u8   pad[3];
-};      /* level 0x10b */
+} __attribute__((packed));      /* level 0x10b */
 
 /* POSIX ACL set/query path info structures */
 #define CIFS_ACL_VERSION 1
@@ -1738,7 +1898,7 @@ struct cifs_posix_ace { /* access control entry (ACE) */
        __u8  cifs_e_tag;
        __u8  cifs_e_perm;
        __le64 cifs_uid; /* or gid */
-}; 
+} __attribute__((packed))
 
 struct cifs_posix_acl { /* access conrol list  (ACL) */
        __le16  version;
@@ -1747,7 +1907,7 @@ struct cifs_posix_acl { /* access conrol list  (ACL) */
        struct cifs_posix_ace ace_array[0];
        /* followed by
        struct cifs_posix_ace default_ace_arraay[] */
-};  /* level 0x204 */
+} __attribute__((packed));  /* level 0x204 */
 
 /* types of access control entries already defined in posix_acl.h */
 /* #define CIFS_POSIX_ACL_USER_OBJ      0x01
@@ -1766,15 +1926,15 @@ struct cifs_posix_acl { /* access conrol list  (ACL) */
 
 struct file_internal_info {
        __u64  UniqueId; /* inode number */
-};      /* level 0x3ee */
+} __attribute__((packed));      /* level 0x3ee */
 struct file_mode_info {
        __le32  Mode;
-};      /* level 0x3f8 */
+} __attribute__((packed));      /* level 0x3f8 */
 
 struct file_attrib_tag {
        __le32 Attribute;
        __le32 ReparseTag;
-};      /* level 0x40b */
+} __attribute__((packed));      /* level 0x40b */
 
 
 /********************************************************/
@@ -1798,7 +1958,7 @@ typedef struct {
        __le64 Permissions;
        __le64 Nlinks;
        char FileName[1];
-} FILE_UNIX_INFO; /* level 0x202 */
+} __attribute__((packed)) FILE_UNIX_INFO; /* level 0x202 */
 
 typedef struct {
        __le32 NextEntryOffset;
@@ -1812,7 +1972,7 @@ typedef struct {
        __le32 ExtFileAttributes;
        __le32 FileNameLength;
        char FileName[1];
-} FILE_DIRECTORY_INFO;   /* level 0x101 FF response data area */
+} __attribute__((packed)) FILE_DIRECTORY_INFO;   /* level 0x101 FF response data area */
 
 typedef struct {
        __le32 NextEntryOffset;
@@ -1827,7 +1987,7 @@ typedef struct {
        __le32 FileNameLength;
        __le32 EaSize; /* length of the xattrs */
        char FileName[1];
-} FILE_FULL_DIRECTORY_INFO;   /* level 0x102 FF response data area */
+} __attribute__((packed)) FILE_FULL_DIRECTORY_INFO;   /* level 0x102 FF response data area */
 
 typedef struct {
        __le32 NextEntryOffset;
@@ -1844,7 +2004,7 @@ typedef struct {
        __le32 Reserved;
        __u64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/
        char FileName[1];
-} SEARCH_ID_FULL_DIR_INFO;   /* level 0x105 FF response data area */
+} __attribute__((packed)) SEARCH_ID_FULL_DIR_INFO;   /* level 0x105 FF response data area */
 
 typedef struct {
        __le32 NextEntryOffset;
@@ -1862,18 +2022,18 @@ typedef struct {
        __u8   Reserved;
        __u8   ShortName[12];
        char FileName[1];
-} FILE_BOTH_DIRECTORY_INFO;   /* level 0x104 FF response data area */
+} __attribute__((packed)) FILE_BOTH_DIRECTORY_INFO;   /* level 0x104 FF response data area */
 
 
 struct gea {
        unsigned char name_len;
        char name[1];
-};
+} __attribute__((packed));
 
 struct gealist {
        unsigned long list_len;
        struct gea list[1];
-};
+} __attribute__((packed));
 
 struct fea {
        unsigned char EA_flags;
@@ -1881,21 +2041,21 @@ struct fea {
        __le16 value_len;
        char name[1];
        /* optionally followed by value */
-};
+} __attribute__((packed));
 /* flags for _FEA.fEA */
 #define FEA_NEEDEA         0x80        /* need EA bit */
 
 struct fealist {
        __le32 list_len;
        struct fea list[1];
-};
+} __attribute__((packed));
 
 /* used to hold an arbitrary blob of data */
 struct data_blob {
        __u8 *data;
        size_t length;
        void (*free) (struct data_blob * data_blob);
-};
+} __attribute__((packed));
 
 
 #ifdef CONFIG_CIFS_POSIX
@@ -1907,18 +2067,17 @@ struct data_blob {
        perhaps add a CreateDevice - to create Pipes and other special .inodes
        Also note POSIX open flags
        2) Close - to return the last write time to do cache across close more safely
-       3) PosixQFSInfo - to return statfs info
-       4) FindFirst return unique inode number - what about resume key, two forms short (matches readdir) and full (enough info to cache inodes)
-       5) Mkdir - set mode
+       3) FindFirst return unique inode number - what about resume key, two 
+       forms short (matches readdir) and full (enough info to cache inodes)
+       4) Mkdir - set mode
        
        And under consideration: 
-       6) FindClose2 (return nanosecond timestamp ??)
-       7) Use nanosecond timestamps throughout all time fields if 
+       5) FindClose2 (return nanosecond timestamp ??)
+       6) Use nanosecond timestamps throughout all time fields if 
           corresponding attribute flag is set
-       8) sendfile - handle based copy
-       9) Direct i/o
-       10) "POSIX ACL" support
-       11) Misc fcntls?
+       7) sendfile - handle based copy
+       8) Direct i/o
+       9) Misc fcntls?
        
        what about fixing 64 bit alignment
        
@@ -1974,7 +2133,7 @@ struct data_blob {
        
  */
 
-/* xsymlink is a symlink format that can be used
+/* xsymlink is a symlink format (used by MacOS) that can be used
    to save symlink info in a regular file when 
    mounted to operating systems that do not
    support the cifs Unix extensions or EAs (for xattr
@@ -1999,7 +2158,7 @@ struct xsymlink {
        char cr2;        /* \n */
 /* if room left, then end with \n then 0x20s by convention but not required */
        char path[1024];  
-};
+} __attribute__((packed));
 
 typedef struct file_xattr_info {
        /* BB do we need another field for flags? BB */
@@ -2007,7 +2166,7 @@ typedef struct file_xattr_info {
        __u32 xattr_value_len;
        char  xattr_name[0];
        /* followed by xattr_value[xattr_value_len], no pad */
-} FILE_XATTR_INFO;     /* extended attribute, info level 0x205 */
+} __attribute__((packed)) FILE_XATTR_INFO;     /* extended attribute, info level 0x205 */
 
 
 /* flags for chattr command */
@@ -2033,10 +2192,8 @@ typedef struct file_xattr_info {
 typedef struct file_chattr_info {
        __le64  mask; /* list of all possible attribute bits */
        __le64  mode; /* list of actual attribute bits on this inode */
-} FILE_CHATTR_INFO;  /* ext attributes (chattr, chflags) level 0x206 */
+} __attribute__((packed)) FILE_CHATTR_INFO;  /* ext attributes (chattr, chflags) level 0x206 */
 
 #endif 
 
-#pragma pack()                 /* resume default structure packing */
-
 #endif                         /* _CIFSPDU_H */
index ea239dea571e1f0a02374079ca0e918bf072cb01..d301149b1bb0f808fc519e5f66faf45a935ea121 100644 (file)
@@ -47,19 +47,24 @@ extern int SendReceive(const unsigned int /* xid */ , struct cifsSesInfo *,
                        struct smb_hdr * /* input */ ,
                        struct smb_hdr * /* out */ ,
                        int * /* bytes returned */ , const int long_op);
+extern int SendReceive2(const unsigned int /* xid */ , struct cifsSesInfo *,
+                       struct kvec *, int /* nvec */,
+                       int * /* bytes returned */ , const int long_op);
 extern int checkSMBhdr(struct smb_hdr *smb, __u16 mid);
 extern int checkSMB(struct smb_hdr *smb, __u16 mid, int length);
 extern int is_valid_oplock_break(struct smb_hdr *smb);
 extern int is_size_safe_to_change(struct cifsInodeInfo *);
+extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *);
 extern unsigned int smbCalcSize(struct smb_hdr *ptr);
+extern unsigned int smbCalcSize_LE(struct smb_hdr *ptr);
 extern int decode_negTokenInit(unsigned char *security_blob, int length,
                        enum securityEnum *secType);
 extern int cifs_inet_pton(int, char * source, void *dst);
 extern int map_smb_to_linux_error(struct smb_hdr *smb);
 extern void header_assemble(struct smb_hdr *, char /* command */ ,
-                       const struct cifsTconInfo *, int /* specifies length
-                           of fixed section (word count) in two byte units */
-                       );
+                           const struct cifsTconInfo *, int /* length of
+                           fixed section (word count) in two byte units */);
+extern __u16 GetNextMid(struct TCP_Server_Info *server);
 extern struct oplock_q_entry * AllocOplockQEntry(struct inode *, u16, 
                                                 struct cifsTconInfo *);
 extern void DeleteOplockQEntry(struct oplock_q_entry *);
@@ -89,7 +94,7 @@ extern int CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
 
 extern int CIFSFindFirst(const int xid, struct cifsTconInfo *tcon,
             const char *searchName, const struct nls_table *nls_codepage,
-            __u16 *searchHandle, struct cifs_search_info * psrch_inf, int map);
+            __u16 *searchHandle, struct cifs_search_info * psrch_inf, int map, const char dirsep);
 
 extern int CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
             __u16 searchHandle, struct cifs_search_info * psrch_inf);
@@ -101,6 +106,10 @@ extern int CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon,
                        const unsigned char *searchName,
                        FILE_ALL_INFO * findData,
                        const struct nls_table *nls_codepage, int remap);
+extern int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon,
+                        const unsigned char *searchName,
+                        FILE_ALL_INFO * findData,
+                        const struct nls_table *nls_codepage, int remap);
 
 extern int CIFSSMBUnixQPathInfo(const int xid,
                        struct cifsTconInfo *tcon,
@@ -125,6 +134,11 @@ extern int get_dfs_path(int xid, struct cifsSesInfo *pSesInfo,
                        int remap);
 extern int CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon,
                        struct kstatfs *FSData);
+extern int SMBOldQFSInfo(const int xid, struct cifsTconInfo *tcon,
+                       struct kstatfs *FSData);
+extern int CIFSSMBSetFSUnixInfo(const int xid, struct cifsTconInfo *tcon,
+                       __u64 cap);
+
 extern int CIFSSMBQFSAttributeInfo(const int xid,
                        struct cifsTconInfo *tcon);
 extern int CIFSSMBQFSDeviceInfo(const int xid, struct cifsTconInfo *tcon);
@@ -207,6 +221,11 @@ extern int CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon,
                        const int access_flags, const int omode,
                        __u16 * netfid, int *pOplock, FILE_ALL_INFO *,
                        const struct nls_table *nls_codepage, int remap);
+extern int SMBLegacyOpen(const int xid, struct cifsTconInfo *tcon,
+                       const char *fileName, const int disposition,
+                       const int access_flags, const int omode,
+                       __u16 * netfid, int *pOplock, FILE_ALL_INFO *,
+                       const struct nls_table *nls_codepage, int remap);
 extern int CIFSSMBClose(const int xid, struct cifsTconInfo *tcon,
                        const int smb_file_id);
 
@@ -222,7 +241,7 @@ extern int CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
 extern int CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
                        const int netfid, const unsigned int count,
                        const __u64 offset, unsigned int *nbytes, 
-                       const char __user *buf,const int long_op);
+                       struct kvec *iov, const int nvec, const int long_op);
 extern int CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon,
                        const unsigned char *searchName, __u64 * inode_number,
                        const struct nls_table *nls_codepage, 
@@ -264,7 +283,8 @@ extern int CIFSSMBCopy(int xid,
                        int remap_special_chars);
 extern int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon, 
                        const int notify_subdirs,const __u16 netfid,
-                       __u32 filter, const struct nls_table *nls_codepage);
+                       __u32 filter, struct file * file, int multishot, 
+                       const struct nls_table *nls_codepage);
 extern ssize_t CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon,
                        const unsigned char *searchName, char * EAData,
                        size_t bufsize, const struct nls_table *nls_codepage,
index 0db0b313d7150f49795c0cc2a765d1abe298f0b0..9312bfc5668202218f59efda0028f92ac5761f40 100644 (file)
@@ -125,6 +125,9 @@ small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
                                rc = CIFSTCon(0, tcon->ses, tcon->treeName, tcon
                                        , nls_codepage);
                                up(&tcon->ses->sesSem);
+                               /* BB FIXME add code to check if wsize needs
+                                  update due to negotiated smb buffer size
+                                  shrinking */
                                if(rc == 0)
                                        atomic_inc(&tconInfoReconnectCount);
 
@@ -166,11 +169,9 @@ small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
 
        header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon,wct);
 
-#ifdef CONFIG_CIFS_STATS
-        if(tcon != NULL) {
-                atomic_inc(&tcon->num_smbs_sent);
-        }
-#endif /* CONFIG_CIFS_STATS */
+        if(tcon != NULL)
+                cifs_stats_inc(&tcon->num_smbs_sent);
+
        return rc;
 }  
 
@@ -222,6 +223,9 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
                                rc = CIFSTCon(0, tcon->ses, tcon->treeName,
                                              tcon, nls_codepage);
                                up(&tcon->ses->sesSem);
+                               /* BB FIXME add code to check if wsize needs
+                               update due to negotiated smb buffer size
+                               shrinking */
                                if(rc == 0)
                                        atomic_inc(&tconInfoReconnectCount);
 
@@ -269,11 +273,9 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
        header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon,
                        wct /*wct */ );
 
-#ifdef CONFIG_CIFS_STATS
-        if(tcon != NULL) {
-                atomic_inc(&tcon->num_smbs_sent);
-        }
-#endif /* CONFIG_CIFS_STATS */
+        if(tcon != NULL)
+                cifs_stats_inc(&tcon->num_smbs_sent);
+
        return rc;
 }
 
@@ -330,7 +332,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
                      (void **) &pSMB, (void **) &pSMBr);
        if (rc)
                return rc;
-
+       pSMB->hdr.Mid = GetNextMid(server);
        pSMB->hdr.Flags2 |= SMBFLG2_UNICODE;
        if (extended_security)
                pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
@@ -422,8 +424,8 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
                }
                                
        }
-       if (pSMB)
-               cifs_buf_release(pSMB);
+       
+       cifs_buf_release(pSMB);
        return rc;
 }
 
@@ -518,6 +520,8 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
        smb_buffer_response = (struct smb_hdr *)pSMB; /* BB removeme BB */
        
        if(ses->server) {
+               pSMB->hdr.Mid = GetNextMid(ses->server);
+
                if(ses->server->secMode & 
                   (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
                        pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
@@ -537,9 +541,8 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
                        rc = -ESHUTDOWN;
                }
        }
-       if (pSMB)
-               cifs_small_buf_release(pSMB);
        up(&ses->sesSem);
+       cifs_small_buf_release(pSMB);
 
        /* if session dead then we do not need to do ulogoff,
                since server closed smb session, no sense reporting 
@@ -583,14 +586,10 @@ DelFileRetry:
        pSMB->ByteCount = cpu_to_le16(name_len + 1);
        rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
                         (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+       cifs_stats_inc(&tcon->num_deletes);
        if (rc) {
                cFYI(1, ("Error in RMFile = %d", rc));
        } 
-#ifdef CONFIG_CIFS_STATS
-        else {
-               atomic_inc(&tcon->num_deletes);
-        }
-#endif
 
        cifs_buf_release(pSMB);
        if (rc == -EAGAIN)
@@ -632,14 +631,10 @@ RmDirRetry:
        pSMB->ByteCount = cpu_to_le16(name_len + 1);
        rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
                         (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+       cifs_stats_inc(&tcon->num_rmdirs);
        if (rc) {
                cFYI(1, ("Error in RMDir = %d", rc));
        }
-#ifdef CONFIG_CIFS_STATS
-        else {
-               atomic_inc(&tcon->num_rmdirs);
-        }
-#endif
 
        cifs_buf_release(pSMB);
        if (rc == -EAGAIN)
@@ -680,20 +675,161 @@ MkDirRetry:
        pSMB->ByteCount = cpu_to_le16(name_len + 1);
        rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
                         (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+       cifs_stats_inc(&tcon->num_mkdirs);
        if (rc) {
                cFYI(1, ("Error in Mkdir = %d", rc));
        }
-#ifdef CONFIG_CIFS_STATS
-        else {
-               atomic_inc(&tcon->num_mkdirs);
-        }
-#endif
+
        cifs_buf_release(pSMB);
        if (rc == -EAGAIN)
                goto MkDirRetry;
        return rc;
 }
 
+static __u16 convert_disposition(int disposition)
+{
+       __u16 ofun = 0;
+
+       switch (disposition) {
+               case FILE_SUPERSEDE:
+                       ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
+                       break;
+               case FILE_OPEN:
+                       ofun = SMBOPEN_OAPPEND;
+                       break;
+               case FILE_CREATE:
+                       ofun = SMBOPEN_OCREATE;
+                       break;
+               case FILE_OPEN_IF:
+                       ofun = SMBOPEN_OCREATE | SMBOPEN_OAPPEND;
+                       break;
+               case FILE_OVERWRITE:
+                       ofun = SMBOPEN_OTRUNC;
+                       break;
+               case FILE_OVERWRITE_IF:
+                       ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
+                       break;
+               default:
+                       cFYI(1,("unknown disposition %d",disposition));
+                       ofun =  SMBOPEN_OAPPEND; /* regular open */
+       }
+       return ofun;
+}
+
+int
+SMBLegacyOpen(const int xid, struct cifsTconInfo *tcon,
+           const char *fileName, const int openDisposition,
+           const int access_flags, const int create_options, __u16 * netfid,
+            int *pOplock, FILE_ALL_INFO * pfile_info,
+           const struct nls_table *nls_codepage, int remap)
+{
+       int rc = -EACCES;
+       OPENX_REQ *pSMB = NULL;
+       OPENX_RSP *pSMBr = NULL;
+       int bytes_returned;
+       int name_len;
+       __u16 count;
+
+OldOpenRetry:
+       rc = smb_init(SMB_COM_OPEN_ANDX, 15, tcon, (void **) &pSMB,
+                     (void **) &pSMBr);
+       if (rc)
+               return rc;
+
+       pSMB->AndXCommand = 0xFF;       /* none */
+
+       if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+               count = 1;      /* account for one byte pad to word boundary */
+               name_len =
+                  cifsConvertToUCS((__le16 *) (pSMB->fileName + 1),
+                                   fileName, PATH_MAX, nls_codepage, remap);
+               name_len++;     /* trailing null */
+               name_len *= 2;
+       } else {                /* BB improve check for buffer overruns BB */
+               count = 0;      /* no pad */
+               name_len = strnlen(fileName, PATH_MAX);
+               name_len++;     /* trailing null */
+               strncpy(pSMB->fileName, fileName, name_len);
+       }
+       if (*pOplock & REQ_OPLOCK)
+               pSMB->OpenFlags = cpu_to_le16(REQ_OPLOCK);
+       else if (*pOplock & REQ_BATCHOPLOCK) {
+               pSMB->OpenFlags = cpu_to_le16(REQ_BATCHOPLOCK);
+       }
+       pSMB->OpenFlags |= cpu_to_le16(REQ_MORE_INFO);
+       /* BB fixme add conversion for access_flags to bits 0 - 2 of mode */
+       /* 0 = read
+          1 = write
+          2 = rw
+          3 = execute
+        */
+       pSMB->Mode = cpu_to_le16(2);
+       pSMB->Mode |= cpu_to_le16(0x40); /* deny none */
+       /* set file as system file if special file such
+          as fifo and server expecting SFU style and
+          no Unix extensions */
+
+        if(create_options & CREATE_OPTION_SPECIAL)
+                pSMB->FileAttributes = cpu_to_le16(ATTR_SYSTEM);
+        else
+                pSMB->FileAttributes = cpu_to_le16(0/*ATTR_NORMAL*/); /* BB FIXME */
+
+       /* if ((omode & S_IWUGO) == 0)
+               pSMB->FileAttributes |= cpu_to_le32(ATTR_READONLY);*/
+       /*  Above line causes problems due to vfs splitting create into two
+           pieces - need to set mode after file created not while it is
+           being created */
+
+       /* BB FIXME BB */
+/*     pSMB->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK); */
+       /* BB FIXME END BB */
+
+       pSMB->Sattr = cpu_to_le16(ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY);
+       pSMB->OpenFunction = cpu_to_le16(convert_disposition(openDisposition));
+       count += name_len;
+       pSMB->hdr.smb_buf_length += count;
+
+       pSMB->ByteCount = cpu_to_le16(count);
+       /* long_op set to 1 to allow for oplock break timeouts */
+       rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+                        (struct smb_hdr *) pSMBr, &bytes_returned, 1);
+       cifs_stats_inc(&tcon->num_opens);
+       if (rc) {
+               cFYI(1, ("Error in Open = %d", rc));
+       } else {
+       /* BB verify if wct == 15 */
+
+/*             *pOplock = pSMBr->OplockLevel; */  /* BB take from action field BB */
+
+               *netfid = pSMBr->Fid;   /* cifs fid stays in le */
+               /* Let caller know file was created so we can set the mode. */
+               /* Do we care about the CreateAction in any other cases? */
+       /* BB FIXME BB */
+/*             if(cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
+                       *pOplock |= CIFS_CREATE_ACTION; */
+       /* BB FIXME END */
+
+               if(pfile_info) {
+                       pfile_info->CreationTime = 0; /* BB convert CreateTime*/
+                       pfile_info->LastAccessTime = 0; /* BB fixme */
+                       pfile_info->LastWriteTime = 0; /* BB fixme */
+                       pfile_info->ChangeTime = 0;  /* BB fixme */
+                       pfile_info->Attributes =
+                               cpu_to_le32(le16_to_cpu(pSMBr->FileAttributes)); 
+                       /* the file_info buf is endian converted by caller */
+                       pfile_info->AllocationSize =
+                               cpu_to_le64(le32_to_cpu(pSMBr->EndOfFile));
+                       pfile_info->EndOfFile = pfile_info->AllocationSize;
+                       pfile_info->NumberOfLinks = cpu_to_le32(1);
+               }
+       }
+
+       cifs_buf_release(pSMB);
+       if (rc == -EAGAIN)
+               goto OldOpenRetry;
+       return rc;
+}
+
 int
 CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon,
            const char *fileName, const int openDisposition,
@@ -738,7 +874,13 @@ openRetry:
        }
        pSMB->DesiredAccess = cpu_to_le32(access_flags);
        pSMB->AllocationSize = 0;
-       pSMB->FileAttributes = cpu_to_le32(ATTR_NORMAL);
+       /* set file as system file if special file such
+          as fifo and server expecting SFU style and
+          no Unix extensions */
+       if(create_options & CREATE_OPTION_SPECIAL)
+               pSMB->FileAttributes = cpu_to_le32(ATTR_SYSTEM);
+       else
+               pSMB->FileAttributes = cpu_to_le32(ATTR_NORMAL);
        /* XP does not handle ATTR_POSIX_SEMANTICS */
        /* but it helps speed up case sensitive checks for other
        servers such as Samba */
@@ -752,7 +894,7 @@ openRetry:
                being created */
        pSMB->ShareAccess = cpu_to_le32(FILE_SHARE_ALL);
        pSMB->CreateDisposition = cpu_to_le32(openDisposition);
-       pSMB->CreateOptions = cpu_to_le32(create_options);
+       pSMB->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK);
        /* BB Expirement with various impersonation levels and verify */
        pSMB->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION);
        pSMB->SecurityFlags =
@@ -765,6 +907,7 @@ openRetry:
        /* long_op set to 1 to allow for oplock break timeouts */
        rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
                         (struct smb_hdr *) pSMBr, &bytes_returned, 1);
+       cifs_stats_inc(&tcon->num_opens);
        if (rc) {
                cFYI(1, ("Error in Open = %d", rc));
        } else {
@@ -782,11 +925,8 @@ openRetry:
                    pfile_info->EndOfFile = pSMBr->EndOfFile;
                    pfile_info->NumberOfLinks = cpu_to_le32(1);
                }
-
-#ifdef CONFIG_CIFS_STATS
-               atomic_inc(&tcon->num_opens);
-#endif
        }
+
        cifs_buf_release(pSMB);
        if (rc == -EAGAIN)
                goto openRetry;
@@ -807,11 +947,16 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon,
        READ_RSP *pSMBr = NULL;
        char *pReadData = NULL;
        int bytes_returned;
+       int wct;
 
        cFYI(1,("Reading %d bytes on fid %d",count,netfid));
+       if(tcon->ses->capabilities & CAP_LARGE_FILES)
+               wct = 12;
+       else
+               wct = 10; /* old style read */
 
        *nbytes = 0;
-       rc = smb_init(SMB_COM_READ_ANDX, 12, tcon, (void **) &pSMB,
+       rc = smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **) &pSMB,
                      (void **) &pSMBr);
        if (rc)
                return rc;
@@ -823,14 +968,26 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon,
        pSMB->AndXCommand = 0xFF;       /* none */
        pSMB->Fid = netfid;
        pSMB->OffsetLow = cpu_to_le32(lseek & 0xFFFFFFFF);
-       pSMB->OffsetHigh = cpu_to_le32(lseek >> 32);
+       if(wct == 12)
+               pSMB->OffsetHigh = cpu_to_le32(lseek >> 32);
+        else if((lseek >> 32) > 0) /* can not handle this big offset for old */
+                return -EIO;
+
        pSMB->Remaining = 0;
        pSMB->MaxCount = cpu_to_le16(count & 0xFFFF);
        pSMB->MaxCountHigh = cpu_to_le32(count >> 16);
-       pSMB->ByteCount = 0;  /* no need to do le conversion since it is 0 */
-
+       if(wct == 12)
+               pSMB->ByteCount = 0;  /* no need to do le conversion since 0 */
+       else {
+               /* old style read */
+               struct smb_com_readx_req * pSMBW = 
+                       (struct smb_com_readx_req *)pSMB;
+               pSMBW->ByteCount = 0;   
+       }
+       
        rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
                         (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+       cifs_stats_inc(&tcon->num_reads);
        if (rc) {
                cERROR(1, ("Send error in read = %d", rc));
        } else {
@@ -876,12 +1033,20 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
        int rc = -EACCES;
        WRITE_REQ *pSMB = NULL;
        WRITE_RSP *pSMBr = NULL;
-       int bytes_returned;
+       int bytes_returned, wct;
        __u32 bytes_sent;
        __u16 byte_count;
 
        /* cFYI(1,("write at %lld %d bytes",offset,count));*/
-       rc = smb_init(SMB_COM_WRITE_ANDX, 14, tcon, (void **) &pSMB,
+       if(tcon->ses == NULL)
+               return -ECONNABORTED;
+
+       if(tcon->ses->capabilities & CAP_LARGE_FILES)
+               wct = 14;
+       else
+               wct = 12;
+
+       rc = smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB,
                      (void **) &pSMBr);
        if (rc)
                return rc;
@@ -892,7 +1057,11 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
        pSMB->AndXCommand = 0xFF;       /* none */
        pSMB->Fid = netfid;
        pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
-       pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
+       if(wct == 14) 
+               pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
+       else if((offset >> 32) > 0) /* can not handle this big offset for old */
+               return -EIO;
+       
        pSMB->Reserved = 0xFFFFFFFF;
        pSMB->WriteMode = 0;
        pSMB->Remaining = 0;
@@ -911,7 +1080,7 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
        if (bytes_sent > count)
                bytes_sent = count;
        pSMB->DataOffset =
-           cpu_to_le16(offsetof(struct smb_com_write_req,Data) - 4);
+               cpu_to_le16(offsetof(struct smb_com_write_req,Data) - 4);
        if(buf)
            memcpy(pSMB->Data,buf,bytes_sent);
        else if(ubuf) {
@@ -919,20 +1088,31 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
                        cifs_buf_release(pSMB);
                        return -EFAULT;
                }
-       } else {
+       } else if (count != 0) {
                /* No buffer */
                cifs_buf_release(pSMB);
                return -EINVAL;
+       } /* else setting file size with write of zero bytes */
+       if(wct == 14)
+               byte_count = bytes_sent + 1; /* pad */
+       else /* wct == 12 */ {
+               byte_count = bytes_sent + 5; /* bigger pad, smaller smb hdr */
        }
-
-       byte_count = bytes_sent + 1 /* pad */ ; /* BB fix this for sends > 64K */
        pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF);
        pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16);
-       pSMB->hdr.smb_buf_length += bytes_sent+1;
-       pSMB->ByteCount = cpu_to_le16(byte_count);
+       pSMB->hdr.smb_buf_length += byte_count;
+
+       if(wct == 14)
+               pSMB->ByteCount = cpu_to_le16(byte_count);
+       else { /* old style write has byte count 4 bytes earlier so 4 bytes pad  */
+               struct smb_com_writex_req * pSMBW = 
+                       (struct smb_com_writex_req *)pSMB;
+               pSMBW->ByteCount = cpu_to_le16(byte_count);
+       }
 
        rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
                         (struct smb_hdr *) pSMBr, &bytes_returned, long_op);
+       cifs_stats_inc(&tcon->num_writes);
        if (rc) {
                cFYI(1, ("Send error in write = %d", rc));
                *nbytes = 0;
@@ -951,56 +1131,72 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
 }
 
 #ifdef CONFIG_CIFS_EXPERIMENTAL
-int CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
+int
+CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
             const int netfid, const unsigned int count,
-            const __u64 offset, unsigned int *nbytes, const char __user *buf,
-            const int long_op)
+            const __u64 offset, unsigned int *nbytes, struct kvec *iov,
+            int n_vec, const int long_op)
 {
        int rc = -EACCES;
        WRITE_REQ *pSMB = NULL;
-       WRITE_RSP *pSMBr = NULL;
-       /*int bytes_returned;*/
-       unsigned bytes_sent;
-       __u16 byte_count;
+       int bytes_returned, wct;
+       int smb_hdr_len;
 
-       rc = small_smb_init(SMB_COM_WRITE_ANDX, 14, tcon, (void **) &pSMB);
-    
+       cFYI(1,("write2 at %lld %d bytes",offset,count)); /* BB removeme BB */
+       if(tcon->ses->capabilities & CAP_LARGE_FILES)
+               wct = 14;
+       else
+               wct = 12;
+       rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB);
        if (rc)
                return rc;
-       
-       pSMBr = (WRITE_RSP *)pSMB; /* BB removeme BB */
-
        /* tcon and ses pointer are checked in smb_init */
        if (tcon->ses->server == NULL)
                return -ECONNABORTED;
 
-       pSMB->AndXCommand = 0xFF; /* none */
+       pSMB->AndXCommand = 0xFF;       /* none */
        pSMB->Fid = netfid;
        pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
-       pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
+       if(wct == 14)
+               pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
+       else if((offset >> 32) > 0) /* can not handle this big offset for old */
+               return -EIO;
        pSMB->Reserved = 0xFFFFFFFF;
        pSMB->WriteMode = 0;
        pSMB->Remaining = 0;
-       bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & ~0xFF;
-       if (bytes_sent > count)
-               bytes_sent = count;
-       pSMB->DataLengthHigh = 0;
+
        pSMB->DataOffset =
            cpu_to_le16(offsetof(struct smb_com_write_req,Data) - 4);
 
-       byte_count = bytes_sent + 1 /* pad */ ;
-       pSMB->DataLengthLow = cpu_to_le16(bytes_sent);
-       pSMB->DataLengthHigh = 0;
-       pSMB->hdr.smb_buf_length += byte_count;
-       pSMB->ByteCount = cpu_to_le16(byte_count);
+       pSMB->DataLengthLow = cpu_to_le16(count & 0xFFFF);
+       pSMB->DataLengthHigh = cpu_to_le16(count >> 16);
+       smb_hdr_len = pSMB->hdr.smb_buf_length + 1; /* hdr + 1 byte pad */
+       if(wct == 14)
+               pSMB->hdr.smb_buf_length += count+1;
+       else /* wct == 12 */
+               pSMB->hdr.smb_buf_length += count+5; /* smb data starts later */ 
+       if(wct == 14)
+               pSMB->ByteCount = cpu_to_le16(count + 1);
+       else /* wct == 12 */ /* bigger pad, smaller smb hdr, keep offset ok */ {
+               struct smb_com_writex_req * pSMBW =
+                               (struct smb_com_writex_req *)pSMB;
+               pSMBW->ByteCount = cpu_to_le16(count + 5);
+       }
+       iov[0].iov_base = pSMB;
+       iov[0].iov_len = smb_hdr_len + 4;
 
-/*     rc = SendReceive2(xid, tcon->ses, (struct smb_hdr *) pSMB,
-                        (struct smb_hdr *) pSMBr, buf, buflen, &bytes_returned, long_op); */  /* BB fixme BB */
+       rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &bytes_returned,
+                         long_op);
+       cifs_stats_inc(&tcon->num_writes);
        if (rc) {
-               cFYI(1, ("Send error in write2 (large write) = %d", rc));
+               cFYI(1, ("Send error Write2 = %d", rc));
                *nbytes = 0;
-       } else
-               *nbytes = le16_to_cpu(pSMBr->Count);
+       } else {
+               WRITE_RSP * pSMBr = (WRITE_RSP *)pSMB;
+               *nbytes = le16_to_cpu(pSMBr->CountHigh);
+               *nbytes = (*nbytes) << 16;
+               *nbytes += le16_to_cpu(pSMBr->Count);
+       }
 
        cifs_small_buf_release(pSMB);
 
@@ -1009,6 +1205,8 @@ int CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
 
        return rc;
 }
+
+
 #endif /* CIFS_EXPERIMENTAL */
 
 int
@@ -1065,7 +1263,7 @@ CIFSSMBLock(const int xid, struct cifsTconInfo *tcon,
 
        rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
                         (struct smb_hdr *) pSMBr, &bytes_returned, timeout);
-
+       cifs_stats_inc(&tcon->num_locks);
        if (rc) {
                cFYI(1, ("Send error in Lock = %d", rc));
        }
@@ -1099,6 +1297,7 @@ CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id)
        pSMB->ByteCount = 0;
        rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
                         (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+       cifs_stats_inc(&tcon->num_closes);
        if (rc) {
                if(rc!=-EINTR) {
                        /* EINTR is expected when user ctl-c to kill app */
@@ -1171,16 +1370,11 @@ renameRetry:
 
        rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
                         (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+       cifs_stats_inc(&tcon->num_renames);
        if (rc) {
                cFYI(1, ("Send error in rename = %d", rc));
        } 
 
-#ifdef CONFIG_CIFS_STATS
-         else {
-               atomic_inc(&tcon->num_renames);
-       }
-#endif
-
        cifs_buf_release(pSMB);
 
        if (rc == -EAGAIN)
@@ -1255,14 +1449,11 @@ int CIFSSMBRenameOpenFile(const int xid,struct cifsTconInfo *pTcon,
        pSMB->ByteCount = cpu_to_le16(byte_count);
        rc = SendReceive(xid, pTcon->ses, (struct smb_hdr *) pSMB,
                          (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+       cifs_stats_inc(&pTcon->num_t2renames);
        if (rc) {
                cFYI(1,("Send error in Rename (by file handle) = %d", rc));
        }
-#ifdef CONFIG_CIFS_STATS
-         else {
-               atomic_inc(&pTcon->num_t2renames);
-       }
-#endif
+
        cifs_buf_release(pSMB);
 
        /* Note: On -EAGAIN error only caller can retry on handle based calls
@@ -1416,6 +1607,7 @@ createSymLinkRetry:
        pSMB->ByteCount = cpu_to_le16(byte_count);
        rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
                         (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+       cifs_stats_inc(&tcon->num_symlinks);
        if (rc) {
                cFYI(1,
                     ("Send error in SetPathInfo (create symlink) = %d",
@@ -1505,6 +1697,7 @@ createHardLinkRetry:
        pSMB->ByteCount = cpu_to_le16(byte_count);
        rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
                         (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+       cifs_stats_inc(&tcon->num_hardlinks);
        if (rc) {
                cFYI(1, ("Send error in SetPathInfo (hard link) = %d", rc));
        }
@@ -1575,6 +1768,7 @@ winCreateHardLinkRetry:
 
        rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
                         (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+       cifs_stats_inc(&tcon->num_hardlinks);
        if (rc) {
                cFYI(1, ("Send error in hard link (NT rename) = %d", rc));
        }
@@ -1775,8 +1969,7 @@ CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon,
                }
        }
 qreparse_out:
-       if (pSMB)
-               cifs_buf_release(pSMB);
+       cifs_buf_release(pSMB);
 
        /* Note: On -EAGAIN error only caller can retry on handle based calls
                since file handle passed in no longer valid */
@@ -2165,6 +2358,67 @@ GetExtAttrOut:
 
 #endif /* CONFIG_POSIX */
 
+/* Legacy Query Path Information call for lookup to old servers such
+   as Win9x/WinME */
+int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon,
+                 const unsigned char *searchName,
+                 FILE_ALL_INFO * pFinfo,
+                 const struct nls_table *nls_codepage, int remap)
+{
+       QUERY_INFORMATION_REQ * pSMB;
+       QUERY_INFORMATION_RSP * pSMBr;
+       int rc = 0;
+       int bytes_returned;
+       int name_len;
+
+       cFYI(1, ("In SMBQPath path %s", searchName)); 
+QInfRetry:
+       rc = smb_init(SMB_COM_QUERY_INFORMATION, 0, tcon, (void **) &pSMB,
+                      (void **) &pSMBr);
+       if (rc)
+               return rc;
+
+       if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+               name_len =
+                    cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
+                                     PATH_MAX, nls_codepage, remap);
+               name_len++;     /* trailing null */
+               name_len *= 2;
+       } else {               
+               name_len = strnlen(searchName, PATH_MAX);
+               name_len++;     /* trailing null */
+               strncpy(pSMB->FileName, searchName, name_len);
+       }
+       pSMB->BufferFormat = 0x04;
+       name_len++; /* account for buffer type byte */  
+       pSMB->hdr.smb_buf_length += (__u16) name_len;
+       pSMB->ByteCount = cpu_to_le16(name_len);
+
+       rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+                         (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+       if (rc) {
+               cFYI(1, ("Send error in QueryInfo = %d", rc));
+       } else if (pFinfo) {            /* decode response */
+               memset(pFinfo, 0, sizeof(FILE_ALL_INFO));
+               pFinfo->AllocationSize =
+                       cpu_to_le64(le32_to_cpu(pSMBr->size));
+               pFinfo->EndOfFile = pFinfo->AllocationSize;
+               pFinfo->Attributes =
+                       cpu_to_le32(le16_to_cpu(pSMBr->attr));
+       } else
+               rc = -EIO; /* bad buffer passed in */
+
+       cifs_buf_release(pSMB);
+
+       if (rc == -EAGAIN)
+               goto QInfRetry;
+
+       return rc;
+}
+
+
+
+
 int
 CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon,
                 const unsigned char *searchName,
@@ -2396,7 +2650,7 @@ findUniqueRetry:
        if (rc) {
                cFYI(1, ("Send error in FindFileDirInfo = %d", rc));
        } else {                /* decode response */
-
+               cifs_stats_inc(&tcon->num_ffirst);
                /* BB fill in */
        }
 
@@ -2414,7 +2668,7 @@ CIFSFindFirst(const int xid, struct cifsTconInfo *tcon,
              const char *searchName, 
              const struct nls_table *nls_codepage,
              __u16 *   pnetfid,
-             struct cifs_search_info * psrch_inf, int remap)
+             struct cifs_search_info * psrch_inf, int remap, const char dirsep)
 {
 /* level 257 SMB_ */
        TRANSACTION2_FFIRST_REQ *pSMB = NULL;
@@ -2441,7 +2695,7 @@ findFirstRetry:
                it got remapped to 0xF03A as if it were part of the
                directory name instead of a wildcard */
                name_len *= 2;
-               pSMB->FileName[name_len] = '\\';
+               pSMB->FileName[name_len] = dirsep;
                pSMB->FileName[name_len+1] = 0;
                pSMB->FileName[name_len+2] = '*';
                pSMB->FileName[name_len+3] = 0;
@@ -2455,7 +2709,7 @@ findFirstRetry:
                if(name_len > buffersize-header)
                        free buffer exit; BB */
                strncpy(pSMB->FileName, searchName, name_len);
-               pSMB->FileName[name_len] = '\\';
+               pSMB->FileName[name_len] = dirsep;
                pSMB->FileName[name_len+1] = '*';
                pSMB->FileName[name_len+2] = 0;
                name_len += 3;
@@ -2496,6 +2750,7 @@ findFirstRetry:
 
        rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
                         (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+       cifs_stats_inc(&tcon->num_ffirst);
 
        if (rc) {/* BB add logic to retry regular search if Unix search rejected unexpectedly by server */
                /* BB Add code to handle unsupported level rc */
@@ -2617,7 +2872,7 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
                                                                                               
        rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
                        (struct smb_hdr *) pSMBr, &bytes_returned, 0);
-                                                                                              
+       cifs_stats_inc(&tcon->num_fnext);
        if (rc) {
                if (rc == -EBADF) {
                        psrch_inf->endOfSearch = TRUE;
@@ -2694,6 +2949,7 @@ CIFSFindClose(const int xid, struct cifsTconInfo *tcon, const __u16 searchHandle
        if (rc) {
                cERROR(1, ("Send error in FindClose = %d", rc));
        }
+       cifs_stats_inc(&tcon->num_fclose);
        cifs_small_buf_release(pSMB);
 
        /* Since session is dead, search handle closed on server already */
@@ -2827,7 +3083,10 @@ getDFSRetry:
                      (void **) &pSMBr);
        if (rc)
                return rc;
-
+       
+       /* server pointer checked in called function, 
+       but should never be null here anyway */
+       pSMB->hdr.Mid = GetNextMid(ses->server);
        pSMB->hdr.Tid = ses->ipc_tid;
        pSMB->hdr.Uid = ses->Suid;
        if (ses->capabilities & CAP_STATUS32) {
@@ -2968,6 +3227,92 @@ GetDFSRefExit:
        return rc;
 }
 
+/* Query File System Info such as free space to old servers such as Win 9x */
+int
+SMBOldQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData)
+{
+/* level 0x01 SMB_QUERY_FILE_SYSTEM_INFO */
+       TRANSACTION2_QFSI_REQ *pSMB = NULL;
+       TRANSACTION2_QFSI_RSP *pSMBr = NULL;
+       FILE_SYSTEM_ALLOC_INFO *response_data;
+       int rc = 0;
+       int bytes_returned = 0;
+       __u16 params, byte_count;
+
+       cFYI(1, ("OldQFSInfo"));
+oldQFSInfoRetry:
+       rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+               (void **) &pSMBr);
+       if (rc)
+               return rc;
+       rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+                     (void **) &pSMBr);
+       if (rc)
+               return rc;
+
+       params = 2;     /* level */
+       pSMB->TotalDataCount = 0;
+       pSMB->MaxParameterCount = cpu_to_le16(2);
+       pSMB->MaxDataCount = cpu_to_le16(1000);
+       pSMB->MaxSetupCount = 0;
+       pSMB->Reserved = 0;
+       pSMB->Flags = 0;
+       pSMB->Timeout = 0;
+       pSMB->Reserved2 = 0;
+       byte_count = params + 1 /* pad */ ;
+       pSMB->TotalParameterCount = cpu_to_le16(params);
+       pSMB->ParameterCount = pSMB->TotalParameterCount;
+       pSMB->ParameterOffset = cpu_to_le16(offsetof(
+       struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
+       pSMB->DataCount = 0;
+       pSMB->DataOffset = 0;
+       pSMB->SetupCount = 1;
+       pSMB->Reserved3 = 0;
+       pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
+       pSMB->InformationLevel = cpu_to_le16(SMB_INFO_ALLOCATION);
+       pSMB->hdr.smb_buf_length += byte_count;
+       pSMB->ByteCount = cpu_to_le16(byte_count);
+
+       rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+               (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+       if (rc) {
+               cFYI(1, ("Send error in QFSInfo = %d", rc));
+       } else {                /* decode response */
+               rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+
+               if (rc || (pSMBr->ByteCount < 18))
+                       rc = -EIO;      /* bad smb */
+               else {
+                       __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+                       cFYI(1,("qfsinf resp BCC: %d  Offset %d",
+                                pSMBr->ByteCount, data_offset));
+
+                       response_data =
+                               (FILE_SYSTEM_ALLOC_INFO *) 
+                               (((char *) &pSMBr->hdr.Protocol) + data_offset);
+                       FSData->f_bsize =
+                               le16_to_cpu(response_data->BytesPerSector) *
+                               le32_to_cpu(response_data->
+                                       SectorsPerAllocationUnit);
+                       FSData->f_blocks =
+                               le32_to_cpu(response_data->TotalAllocationUnits);
+                       FSData->f_bfree = FSData->f_bavail =
+                               le32_to_cpu(response_data->FreeAllocationUnits);
+                       cFYI(1,
+                            ("Blocks: %lld  Free: %lld Block size %ld",
+                             (unsigned long long)FSData->f_blocks,
+                             (unsigned long long)FSData->f_bfree,
+                             FSData->f_bsize));
+               }
+       }
+       cifs_buf_release(pSMB);
+
+       if (rc == -EAGAIN)
+               goto oldQFSInfoRetry;
+
+       return rc;
+}
+
 int
 CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData)
 {
@@ -2989,7 +3334,7 @@ QFSInfoRetry:
        params = 2;     /* level */
        pSMB->TotalDataCount = 0;
        pSMB->MaxParameterCount = cpu_to_le16(2);
-       pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find exact max SMB PDU from sess structure BB */
+       pSMB->MaxDataCount = cpu_to_le16(1000);
        pSMB->MaxSetupCount = 0;
        pSMB->Reserved = 0;
        pSMB->Flags = 0;
@@ -3012,17 +3357,14 @@ QFSInfoRetry:
        rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
                         (struct smb_hdr *) pSMBr, &bytes_returned, 0);
        if (rc) {
-               cERROR(1, ("Send error in QFSInfo = %d", rc));
+               cFYI(1, ("Send error in QFSInfo = %d", rc));
        } else {                /* decode response */
                 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
 
-               if (rc || (pSMBr->ByteCount < 24)) /* BB alsO CHEck enough total bytes returned */
+               if (rc || (pSMBr->ByteCount < 24))
                        rc = -EIO;      /* bad smb */
                else {
                        __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
-                       cFYI(1,
-                               ("Decoding qfsinfo response.  BCC: %d  Offset %d",
-                               pSMBr->ByteCount, data_offset));
 
                        response_data =
                            (FILE_SYSTEM_INFO
@@ -3257,6 +3599,77 @@ QFSUnixRetry:
        return rc;
 }
 
+int
+CIFSSMBSetFSUnixInfo(const int xid, struct cifsTconInfo *tcon, __u64 cap)
+{
+/* level 0x200  SMB_SET_CIFS_UNIX_INFO */
+       TRANSACTION2_SETFSI_REQ *pSMB = NULL;
+       TRANSACTION2_SETFSI_RSP *pSMBr = NULL;
+       int rc = 0;
+       int bytes_returned = 0;
+       __u16 params, param_offset, offset, byte_count;
+
+       cFYI(1, ("In SETFSUnixInfo"));
+SETFSUnixRetry:
+       rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+                     (void **) &pSMBr);
+       if (rc)
+               return rc;
+
+       params = 4;     /* 2 bytes zero followed by info level. */
+       pSMB->MaxSetupCount = 0;
+       pSMB->Reserved = 0;
+       pSMB->Flags = 0;
+       pSMB->Timeout = 0;
+       pSMB->Reserved2 = 0;
+       param_offset = offsetof(struct smb_com_transaction2_setfsi_req, FileNum) - 4;
+       offset = param_offset + params;
+
+       pSMB->MaxParameterCount = cpu_to_le16(4);
+       pSMB->MaxDataCount = cpu_to_le16(100);  /* BB find exact max SMB PDU from sess structure BB */
+       pSMB->SetupCount = 1;
+       pSMB->Reserved3 = 0;
+       pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FS_INFORMATION);
+       byte_count = 1 /* pad */ + params + 12;
+
+       pSMB->DataCount = cpu_to_le16(12);
+       pSMB->ParameterCount = cpu_to_le16(params);
+       pSMB->TotalDataCount = pSMB->DataCount;
+       pSMB->TotalParameterCount = pSMB->ParameterCount;
+       pSMB->ParameterOffset = cpu_to_le16(param_offset);
+       pSMB->DataOffset = cpu_to_le16(offset);
+
+       /* Params. */
+       pSMB->FileNum = 0;
+       pSMB->InformationLevel = cpu_to_le16(SMB_SET_CIFS_UNIX_INFO);
+
+       /* Data. */
+       pSMB->ClientUnixMajor = cpu_to_le16(CIFS_UNIX_MAJOR_VERSION);
+       pSMB->ClientUnixMinor = cpu_to_le16(CIFS_UNIX_MINOR_VERSION);
+       pSMB->ClientUnixCap = cpu_to_le64(cap);
+
+       pSMB->hdr.smb_buf_length += byte_count;
+       pSMB->ByteCount = cpu_to_le16(byte_count);
+
+       rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+                        (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+       if (rc) {
+               cERROR(1, ("Send error in SETFSUnixInfo = %d", rc));
+       } else {                /* decode response */
+               rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+               if (rc) {
+                       rc = -EIO;      /* bad smb */
+               }
+       }
+       cifs_buf_release(pSMB);
+
+       if (rc == -EAGAIN)
+               goto SETFSUnixRetry;
+
+       return rc;
+}
+
+
 
 int
 CIFSSMBQFSPosixInfo(const int xid, struct cifsTconInfo *tcon,
@@ -3321,16 +3734,16 @@ QFSPosixRetry:
                                        le64_to_cpu(response_data->TotalBlocks);
                        FSData->f_bfree =
                            le64_to_cpu(response_data->BlocksAvail);
-                       if(response_data->UserBlocksAvail == -1) {
+                       if(response_data->UserBlocksAvail == cpu_to_le64(-1)) {
                                FSData->f_bavail = FSData->f_bfree;
                        } else {
                                FSData->f_bavail =
                                        le64_to_cpu(response_data->UserBlocksAvail);
                        }
-                       if(response_data->TotalFileNodes != -1)
+                       if(response_data->TotalFileNodes != cpu_to_le64(-1))
                                FSData->f_files =
                                        le64_to_cpu(response_data->TotalFileNodes);
-                       if(response_data->FreeFileNodes != -1)
+                       if(response_data->FreeFileNodes != cpu_to_le64(-1))
                                FSData->f_ffree =
                                        le64_to_cpu(response_data->FreeFileNodes);
                }
@@ -3376,7 +3789,7 @@ SetEOFRetry:
                                     PATH_MAX, nls_codepage, remap);
                name_len++;     /* trailing null */
                name_len *= 2;
-       } else {                /* BB improve the check for buffer overruns BB */
+       } else {        /* BB improve the check for buffer overruns BB */
                name_len = strnlen(fileName, PATH_MAX);
                name_len++;     /* trailing null */
                strncpy(pSMB->FileName, fileName, name_len);
@@ -3384,7 +3797,7 @@ SetEOFRetry:
        params = 6 + name_len;
        data_count = sizeof (struct file_end_of_file_info);
        pSMB->MaxParameterCount = cpu_to_le16(2);
-       pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB size from sess */
+       pSMB->MaxDataCount = cpu_to_le16(4100);
        pSMB->MaxSetupCount = 0;
        pSMB->Reserved = 0;
        pSMB->Flags = 0;
@@ -3766,7 +4179,7 @@ setPermsRetry:
                                     PATH_MAX, nls_codepage, remap);
                name_len++;     /* trailing null */
                name_len *= 2;
-       } else {                /* BB improve the check for buffer overruns BB */
+       } else {        /* BB improve the check for buffer overruns BB */
                name_len = strnlen(fileName, PATH_MAX);
                name_len++;     /* trailing null */
                strncpy(pSMB->FileName, fileName, name_len);
@@ -3839,12 +4252,14 @@ setPermsRetry:
 }
 
 int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon, 
-                       const int notify_subdirs, const __u16 netfid,
-                       __u32 filter, const struct nls_table *nls_codepage)
+                 const int notify_subdirs, const __u16 netfid,
+                 __u32 filter, struct file * pfile, int multishot, 
+                 const struct nls_table *nls_codepage)
 {
        int rc = 0;
        struct smb_com_transaction_change_notify_req * pSMB = NULL;
        struct smb_com_transaction_change_notify_rsp * pSMBr = NULL;
+       struct dir_notify_req *dnotify_req;
        int bytes_returned;
 
        cFYI(1, ("In CIFSSMBNotify for file handle %d",(int)netfid));
@@ -3877,6 +4292,28 @@ int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon,
                        (struct smb_hdr *) pSMBr, &bytes_returned, -1);
        if (rc) {
                cFYI(1, ("Error in Notify = %d", rc));
+       } else {
+               /* Add file to outstanding requests */
+               /* BB change to kmem cache alloc */     
+               dnotify_req = (struct dir_notify_req *) kmalloc(
+                                               sizeof(struct dir_notify_req),
+                                                GFP_KERNEL);
+               if(dnotify_req) {
+                       dnotify_req->Pid = pSMB->hdr.Pid;
+                       dnotify_req->PidHigh = pSMB->hdr.PidHigh;
+                       dnotify_req->Mid = pSMB->hdr.Mid;
+                       dnotify_req->Tid = pSMB->hdr.Tid;
+                       dnotify_req->Uid = pSMB->hdr.Uid;
+                       dnotify_req->netfid = netfid;
+                       dnotify_req->pfile = pfile;
+                       dnotify_req->filter = filter;
+                       dnotify_req->multishot = multishot;
+                       spin_lock(&GlobalMid_Lock);
+                       list_add_tail(&dnotify_req->lhead, 
+                                       &GlobalDnotifyReqList);
+                       spin_unlock(&GlobalMid_Lock);
+               } else 
+                       rc = -ENOMEM;
        }
        cifs_buf_release(pSMB);
        return rc;      
index 47360156cc54f40a3f9d469725c8dc19331a106f..d74367a08d513dd4cf6bb6a3aa090996972ea7f5 100644 (file)
@@ -29,6 +29,8 @@
 #include <linux/utsname.h>
 #include <linux/mempool.h>
 #include <linux/delay.h>
+#include <linux/completion.h>
+#include <linux/pagevec.h>
 #include <asm/uaccess.h>
 #include <asm/processor.h>
 #include "cifspdu.h"
@@ -44,6 +46,8 @@
 #define CIFS_PORT 445
 #define RFC1001_PORT 139
 
+static DECLARE_COMPLETION(cifsd_complete);
+
 extern void SMBencrypt(unsigned char *passwd, unsigned char *c8,
                       unsigned char *p24);
 extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8,
@@ -60,6 +64,7 @@ struct smb_vol {
        char *in6_addr;  /* ipv6 address as human readable form of in6_addr */
        char *iocharset;  /* local code page for mapping to and from Unicode */
        char source_rfc1001_name[16]; /* netbios name of client */
+       char target_rfc1001_name[16]; /* netbios name of server for Win9x/ME */
        uid_t linux_uid;
        gid_t linux_gid;
        mode_t file_mode;
@@ -74,6 +79,10 @@ struct smb_vol {
        unsigned server_ino:1; /* use inode numbers from server ie UniqueId */
        unsigned direct_io:1;
        unsigned remap:1;   /* set to remap seven reserved chars in filenames */
+       unsigned posix_paths:1;   /* unset to not ask for posix pathnames. */
+       unsigned sfu_emul:1;
+       unsigned nocase;     /* request case insensitive filenames */
+       unsigned nobrl;      /* disable sending byte range locks to srv */
        unsigned int rsize;
        unsigned int wsize;
        unsigned int sockopt;
@@ -82,7 +91,8 @@ struct smb_vol {
 
 static int ipv4_connect(struct sockaddr_in *psin_server, 
                        struct socket **csocket,
-                       char * netb_name);
+                       char * netb_name,
+                       char * server_netb_name);
 static int ipv6_connect(struct sockaddr_in6 *psin_server, 
                        struct socket **csocket);
 
@@ -175,9 +185,11 @@ cifs_reconnect(struct TCP_Server_Info *server)
                } else {
                        rc = ipv4_connect(&server->addr.sockAddr, 
                                        &server->ssocket,
-                                       server->workstation_RFC1001_name);
+                                       server->workstation_RFC1001_name,
+                                       server->server_RFC1001_name);
                }
                if(rc) {
+                       cFYI(1,("reconnect error %d",rc));
                        msleep(3000);
                } else {
                        atomic_inc(&tcpSesReconnectCount);
@@ -293,12 +305,12 @@ static int coalesce_t2(struct smb_hdr * psecond, struct smb_hdr *pTargetSMB)
        byte_count += total_in_buf2;
        BCC_LE(pTargetSMB) = cpu_to_le16(byte_count);
 
-       byte_count = be32_to_cpu(pTargetSMB->smb_buf_length);
+       byte_count = pTargetSMB->smb_buf_length;
        byte_count += total_in_buf2;
 
        /* BB also add check that we are not beyond maximum buffer size */
                
-       pTargetSMB->smb_buf_length = cpu_to_be32(byte_count);
+       pTargetSMB->smb_buf_length = byte_count;
 
        if(remaining == total_in_buf2) {
                cFYI(1,("found the last secondary response"));
@@ -323,7 +335,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
        struct cifsSesInfo *ses;
        struct task_struct *task_to_wake = NULL;
        struct mid_q_entry *mid_entry;
-       char *temp;
+       char temp;
        int isLargeBuf = FALSE;
        int isMultiRsp;
        int reconnect;
@@ -337,6 +349,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
        atomic_inc(&tcpSesAllocCount);
        length = tcpSesAllocCount.counter;
        write_unlock(&GlobalSMBSeslock);
+       complete(&cifsd_complete);
        if(length  > 1) {
                mempool_resize(cifs_req_poolp,
                        length + cifs_min_rcv,
@@ -424,22 +437,32 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
                        continue;
                }
 
-               /* the right amount was read from socket - 4 bytes */
+               /* The right amount was read from socket - 4 bytes */
+               /* so we can now interpret the length field */
+
+               /* the first byte big endian of the length field,
+               is actually not part of the length but the type
+               with the most common, zero, as regular data */
+               temp = *((char *) smb_buffer);
 
+               /* Note that FC 1001 length is big endian on the wire, 
+               but we convert it here so it is always manipulated
+               as host byte order */
                pdu_length = ntohl(smb_buffer->smb_buf_length);
-               cFYI(1,("rfc1002 length(big endian)0x%x)", pdu_length+4));
+               smb_buffer->smb_buf_length = pdu_length;
+
+               cFYI(1,("rfc1002 length 0x%x)", pdu_length+4));
 
-               temp = (char *) smb_buffer;
-               if (temp[0] == (char) RFC1002_SESSION_KEEP_ALIVE) {
+               if (temp == (char) RFC1002_SESSION_KEEP_ALIVE) {
                        continue; 
-               } else if (temp[0] == (char)RFC1002_POSITIVE_SESSION_RESPONSE) {
+               } else if (temp == (char)RFC1002_POSITIVE_SESSION_RESPONSE) {
                        cFYI(1,("Good RFC 1002 session rsp"));
                        continue;
-               } else if (temp[0] == (char)RFC1002_NEGATIVE_SESSION_RESPONSE) {
+               } else if (temp == (char)RFC1002_NEGATIVE_SESSION_RESPONSE) {
                        /* we get this from Windows 98 instead of 
                           an error on SMB negprot response */
                        cFYI(1,("Negative RFC1002 Session Response Error 0x%x)",
-                               temp[4]));
+                               pdu_length));
                        if(server->tcpStatus == CifsNew) {
                                /* if nack on negprot (rather than 
                                ret of smb negprot error) reconnecting
@@ -461,9 +484,10 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
                                wake_up(&server->response_q);
                                continue;
                        }
-               } else if (temp[0] != (char) 0) {
+               } else if (temp != (char) 0) {
                        cERROR(1,("Unknown RFC 1002 frame"));
-                       cifs_dump_mem(" Received Data: ", temp, length);
+                       cifs_dump_mem(" Received Data: ", (char *)smb_buffer,
+                                     length);
                        cifs_reconnect(server);
                        csocket = server->ssocket;
                        continue;
@@ -533,7 +557,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
 
                dump_smb(smb_buffer, length);
                if (checkSMB (smb_buffer, smb_buffer->Mid, total_read+4)) {
-                       cERROR(1, ("Bad SMB Received "));
+                       cifs_dump_mem("Bad SMB: ", smb_buffer, 48);
                        continue;
                }
 
@@ -581,6 +605,9 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
 multi_t2_fnd:
                                task_to_wake = mid_entry->tsk;
                                mid_entry->midState = MID_RESPONSE_RECEIVED;
+#ifdef CONFIG_CIFS_STATS2
+                               mid_entry->when_received = jiffies;
+#endif
                                break;
                        }
                }
@@ -598,7 +625,8 @@ multi_t2_fnd:
                } else if ((is_valid_oplock_break(smb_buffer) == FALSE)
                    && (isMultiRsp == FALSE)) {                          
                        cERROR(1, ("No task to wake, unknown frame rcvd!"));
-                       cifs_dump_mem("Received Data is: ",temp,sizeof(struct smb_hdr));
+                       cifs_dump_mem("Received Data is: ",(char *)smb_buffer,
+                                     sizeof(struct smb_hdr));
                }
        } /* end while !EXITING */
 
@@ -676,7 +704,7 @@ multi_t2_fnd:
                msleep(125);
        }
 
-       if (list_empty(&server->pending_mid_q)) {
+       if (!list_empty(&server->pending_mid_q)) {
                /* mpx threads have not exited yet give them 
                at least the smb send timeout time for long ops */
                /* due to delays on oplock break requests, we need
@@ -713,7 +741,7 @@ multi_t2_fnd:
                        GFP_KERNEL);
        }
        
-       msleep(250);
+       complete_and_exit(&cifsd_complete, 0);
        return 0;
 }
 
@@ -737,7 +765,9 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol)
                        toupper(system_utsname.nodename[i]);
        }
        vol->source_rfc1001_name[15] = 0;
-
+       /* null target name indicates to use *SMBSERVR default called name
+          if we end up sending RFC1001 session initialize */
+       vol->target_rfc1001_name[0] = 0;
        vol->linux_uid = current->uid;  /* current->euid instead? */
        vol->linux_gid = current->gid;
        vol->dir_mode = S_IRWXUGO;
@@ -747,6 +777,9 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol)
        /* vol->retry default is 0 (i.e. "soft" limited retry not hard retry) */
        vol->rw = TRUE;
 
+       /* default is always to request posix paths. */
+       vol->posix_paths = 1;
+
        if (!options)
                return 1;
 
@@ -987,7 +1020,31 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol)
                                /* The string has 16th byte zero still from
                                set at top of the function  */
                                if((i==15) && (value[i] != 0))
-                                       printk(KERN_WARNING "CIFS: netbiosname longer than 15 and was truncated.\n");
+                                       printk(KERN_WARNING "CIFS: netbiosname longer than 15 truncated.\n");
+                       }
+               } else if (strnicmp(data, "servern", 7) == 0) {
+                       /* servernetbiosname specified override *SMBSERVER */
+                       if (!value || !*value || (*value == ' ')) {
+                               cFYI(1,("empty server netbiosname specified"));
+                       } else {
+                               /* last byte, type, is 0x20 for servr type */
+                               memset(vol->target_rfc1001_name,0x20,16);
+
+                               for(i=0;i<15;i++) {
+                               /* BB are there cases in which a comma can be
+                                  valid in this workstation netbios name (and need
+                                  special handling)? */
+
+                               /* user or mount helper must uppercase netbiosname */
+                                       if (value[i]==0)
+                                               break;
+                                       else
+                                               vol->target_rfc1001_name[i] = value[i];
+                               }
+                               /* The string has 16th byte zero still from
+                                  set at top of the function  */
+                               if((i==15) && (value[i] != 0))
+                                       printk(KERN_WARNING "CIFS: server netbiosname longer than 15 truncated.\n");
                        }
                } else if (strnicmp(data, "credentials", 4) == 0) {
                        /* ignore */
@@ -1025,6 +1082,27 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol)
                        vol->remap = 1;
                } else if (strnicmp(data, "nomapchars", 10) == 0) {
                        vol->remap = 0;
+                } else if (strnicmp(data, "sfu", 3) == 0) {
+                        vol->sfu_emul = 1;
+                } else if (strnicmp(data, "nosfu", 5) == 0) {
+                        vol->sfu_emul = 0;
+               } else if (strnicmp(data, "posixpaths", 10) == 0) {
+                       vol->posix_paths = 1;
+               } else if (strnicmp(data, "noposixpaths", 12) == 0) {
+                       vol->posix_paths = 0;
+                } else if ((strnicmp(data, "nocase", 6) == 0) ||
+                          (strnicmp(data, "ignorecase", 10)  == 0)) {
+                        vol->nocase = 1;
+               } else if (strnicmp(data, "brl", 3) == 0) {
+                       vol->nobrl =  0;
+               } else if ((strnicmp(data, "nobrl", 5) == 0) || 
+                          (strnicmp(data, "nolock", 6) == 0)) {
+                       vol->nobrl =  1;
+                       /* turn off mandatory locking in mode
+                       if remote locking is turned off since the
+                       local vfs will do advisory */
+                       if(vol->file_mode == (S_IALLUGO & ~(S_ISUID | S_IXGRP)))
+                               vol->file_mode = S_IALLUGO;
                } else if (strnicmp(data, "setuids", 7) == 0) {
                        vol->setuids = 1;
                } else if (strnicmp(data, "nosetuids", 9) == 0) {
@@ -1244,7 +1322,7 @@ static void rfc1002mangle(char * target,char * source, unsigned int length)
 
 static int
 ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket, 
-                        char * netbios_name)
+            char * netbios_name, char * target_name)
 {
        int rc = 0;
        int connected = 0;
@@ -1309,10 +1387,16 @@ ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket,
        /* Eventually check for other socket options to change from 
                the default. sock_setsockopt not used because it expects 
                user space buffer */
+        cFYI(1,("sndbuf %d rcvbuf %d rcvtimeo 0x%lx",(*csocket)->sk->sk_sndbuf,
+                (*csocket)->sk->sk_rcvbuf, (*csocket)->sk->sk_rcvtimeo));
        (*csocket)->sk->sk_rcvtimeo = 7 * HZ;
+       /* make the bufsizes depend on wsize/rsize and max requests */
+       if((*csocket)->sk->sk_sndbuf < (200 * 1024))
+               (*csocket)->sk->sk_sndbuf = 200 * 1024;
+       if((*csocket)->sk->sk_rcvbuf < (140 * 1024))
+               (*csocket)->sk->sk_rcvbuf = 140 * 1024;
 
        /* send RFC1001 sessinit */
-
        if(psin_server->sin_port == htons(RFC1001_PORT)) {
                /* some servers require RFC1001 sessinit before sending
                negprot - BB check reconnection in case where second 
@@ -1322,8 +1406,14 @@ ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket,
                ses_init_buf = kzalloc(sizeof(struct rfc1002_session_packet), GFP_KERNEL);
                if(ses_init_buf) {
                        ses_init_buf->trailer.session_req.called_len = 32;
-                       rfc1002mangle(ses_init_buf->trailer.session_req.called_name,
-                               DEFAULT_CIFS_CALLED_NAME,16);
+                       if(target_name && (target_name[0] != 0)) {
+                               rfc1002mangle(ses_init_buf->trailer.session_req.called_name,
+                                       target_name, 16);
+                       } else {
+                               rfc1002mangle(ses_init_buf->trailer.session_req.called_name,
+                                       DEFAULT_CIFS_CALLED_NAME,16);
+                       }
+
                        ses_init_buf->trailer.session_req.calling_len = 32;
                        /* calling name ends in null (byte 16) from old smb
                        convention. */
@@ -1556,7 +1646,9 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                        sin_server.sin_port = htons(volume_info.port);
                else
                        sin_server.sin_port = 0;
-               rc = ipv4_connect(&sin_server,&csocket,volume_info.source_rfc1001_name);
+               rc = ipv4_connect(&sin_server,&csocket,
+                                 volume_info.source_rfc1001_name,
+                                 volume_info.target_rfc1001_name);
                if (rc < 0) {
                        cERROR(1,
                               ("Error connecting to IPv4 socket. Aborting operation"));
@@ -1606,9 +1698,11 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                                        kfree(volume_info.password);
                                FreeXid(xid);
                                return rc;
-                       } else
-                               rc = 0;
+                       }
+                       wait_for_completion(&cifsd_complete);
+                       rc = 0;
                        memcpy(srvTcp->workstation_RFC1001_name, volume_info.source_rfc1001_name,16);
+                       memcpy(srvTcp->server_RFC1001_name, volume_info.target_rfc1001_name,16);
                        srvTcp->sequence_number = 0;
                }
        }
@@ -1653,17 +1747,27 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
     
        /* search for existing tcon to this server share */
        if (!rc) {
-               if((volume_info.rsize) && (volume_info.rsize <= CIFSMaxBufSize))
+               if(volume_info.rsize > CIFSMaxBufSize) {
+                       cERROR(1,("rsize %d too large, using MaxBufSize",
+                               volume_info.rsize));
+                       cifs_sb->rsize = CIFSMaxBufSize;
+               } else if((volume_info.rsize) && (volume_info.rsize <= CIFSMaxBufSize))
                        cifs_sb->rsize = volume_info.rsize;
-               else
-                       cifs_sb->rsize = srvTcp->maxBuf - MAX_CIFS_HDR_SIZE; /* default */
-               if((volume_info.wsize) && (volume_info.wsize <= CIFSMaxBufSize))
+               else /* default */
+                       cifs_sb->rsize = CIFSMaxBufSize;
+
+               if(volume_info.wsize > PAGEVEC_SIZE * PAGE_CACHE_SIZE) {
+                       cERROR(1,("wsize %d too large using 4096 instead",
+                                 volume_info.wsize));
+                       cifs_sb->wsize = 4096;
+               } else if(volume_info.wsize)
                        cifs_sb->wsize = volume_info.wsize;
                else
                        cifs_sb->wsize = CIFSMaxBufSize; /* default */
                if(cifs_sb->rsize < PAGE_CACHE_SIZE) {
-                       cifs_sb->rsize = PAGE_CACHE_SIZE;
-                       cERROR(1,("Attempt to set readsize for mount to less than one page (4096)"));
+                       cifs_sb->rsize = PAGE_CACHE_SIZE; 
+                       /* Windows ME does this */
+                       cFYI(1,("Attempt to set readsize for mount to less than one page (4096)"));
                }
                cifs_sb->mnt_uid = volume_info.linux_uid;
                cifs_sb->mnt_gid = volume_info.linux_gid;
@@ -1681,8 +1785,13 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                        cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR;
                if(volume_info.no_xattr)
                        cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR;
+               if(volume_info.sfu_emul)
+                       cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL;
+               if(volume_info.nobrl)
+                       cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL;
+
                if(volume_info.direct_io) {
-                       cERROR(1,("mounting share using direct i/o"));
+                       cFYI(1,("mounting share using direct i/o"));
                        cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO;
                }
 
@@ -1696,6 +1805,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                           to the same server share the last value passed in 
                           for the retry flag is used */
                        tcon->retry = volume_info.retry;
+                       tcon->nocase = volume_info.nocase;
                } else {
                        tcon = tconInfoAlloc();
                        if (tcon == NULL)
@@ -1724,6 +1834,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                                if (!rc) {
                                        atomic_inc(&pSesInfo->inUse);
                                        tcon->retry = volume_info.retry;
+                                       tcon->nocase = volume_info.nocase;
                                }
                        }
                }
@@ -1745,8 +1856,10 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                        spin_lock(&GlobalMid_Lock);
                        srvTcp->tcpStatus = CifsExiting;
                        spin_unlock(&GlobalMid_Lock);
-                       if(srvTcp->tsk)
+                       if(srvTcp->tsk) {
                                send_sig(SIGKILL,srvTcp->tsk,1);
+                               wait_for_completion(&cifsd_complete);
+                       }
                }
                 /* If find_unc succeeded then rc == 0 so we can not end */
                if (tcon)  /* up accidently freeing someone elses tcon struct */
@@ -1759,8 +1872,10 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                                        temp_rc = CIFSSMBLogoff(xid, pSesInfo);
                                        /* if the socketUseCount is now zero */
                                        if((temp_rc == -ESHUTDOWN) &&
-                                          (pSesInfo->server->tsk))
+                                          (pSesInfo->server->tsk)) {
                                                send_sig(SIGKILL,pSesInfo->server->tsk,1);
+                                               wait_for_completion(&cifsd_complete);
+                                       }
                                } else
                                        cFYI(1, ("No session or bad tcon"));
                                sesInfoFree(pSesInfo);
@@ -1783,8 +1898,27 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                                                cFYI(1,("server negotiated posix acl support"));
                                                sb->s_flags |= MS_POSIXACL;
                                }
+
+                               /* Try and negotiate POSIX pathnames if we can. */
+                               if (volume_info.posix_paths && (CIFS_UNIX_POSIX_PATHNAMES_CAP &
+                                   le64_to_cpu(tcon->fsUnixInfo.Capability))) {
+                                       if (!CIFSSMBSetFSUnixInfo(xid, tcon, CIFS_UNIX_POSIX_PATHNAMES_CAP))  {
+                                               cFYI(1,("negotiated posix pathnames support"));
+                                               cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_POSIX_PATHS;
+                                       } else {
+                                               cFYI(1,("posix pathnames support requested but not supported"));
+                                       }
+                               }
                        }
                }
+               if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X))
+                       cifs_sb->wsize = min(cifs_sb->wsize,
+                                            (tcon->ses->server->maxBuf -
+                                             MAX_CIFS_HDR_SIZE));
+               if (!(tcon->ses->capabilities & CAP_LARGE_READ_X))
+                        cifs_sb->rsize = min(cifs_sb->rsize,
+                                             (tcon->ses->server->maxBuf -
+                                              MAX_CIFS_HDR_SIZE));
        }
 
        /* volume_info.password is freed above when existing session found
@@ -1832,6 +1966,7 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses,
        header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
                        NULL /* no tCon exists yet */ , 13 /* wct */ );
 
+       smb_buffer->Mid = GetNextMid(ses->server);
        pSMB->req_no_secext.AndXCommand = 0xFF;
        pSMB->req_no_secext.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
        pSMB->req_no_secext.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
@@ -2107,6 +2242,8 @@ CIFSSpnegoSessSetup(unsigned int xid, struct cifsSesInfo *ses,
        /* send SMBsessionSetup here */
        header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
                        NULL /* no tCon exists yet */ , 12 /* wct */ );
+
+       smb_buffer->Mid = GetNextMid(ses->server);
        pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
        pSMB->req.AndXCommand = 0xFF;
        pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
@@ -2373,6 +2510,8 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid,
        /* send SMBsessionSetup here */
        header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
                        NULL /* no tCon exists yet */ , 12 /* wct */ );
+
+       smb_buffer->Mid = GetNextMid(ses->server);
        pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
        pSMB->req.hdr.Flags |= (SMBFLG_CASELESS | SMBFLG_CANONICAL_PATH_FORMAT);
 
@@ -2715,6 +2854,8 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses,
        /* send SMBsessionSetup here */
        header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
                        NULL /* no tCon exists yet */ , 12 /* wct */ );
+
+       smb_buffer->Mid = GetNextMid(ses->server);
        pSMB->req.hdr.Flags |= (SMBFLG_CASELESS | SMBFLG_CANONICAL_PATH_FORMAT);
        pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
        pSMB->req.AndXCommand = 0xFF;
@@ -3086,6 +3227,8 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
 
        header_assemble(smb_buffer, SMB_COM_TREE_CONNECT_ANDX,
                        NULL /*no tid */ , 4 /*wct */ );
+
+       smb_buffer->Mid = GetNextMid(ses->server);
        smb_buffer->Uid = ses->Suid;
        pSMB = (TCONX_REQ *) smb_buffer;
        pSMBr = (TCONX_RSP *) smb_buffer_response;
@@ -3207,8 +3350,10 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
                                return 0;
                        } else if (rc == -ESHUTDOWN) {
                                cFYI(1,("Waking up socket by sending it signal"));
-                               if(cifsd_task)
+                               if(cifsd_task) {
                                        send_sig(SIGKILL,cifsd_task,1);
+                                       wait_for_completion(&cifsd_complete);
+                               }
                                rc = 0;
                        } /* else - we have an smb session
                                left on this socket do not kill cifsd */
index d335269bd91cfe5b626aba8a45eae25136b450dc..8dfe717a332abaef46800beae64c781b26bdb831 100644 (file)
@@ -48,6 +48,7 @@ build_path_from_dentry(struct dentry *direntry)
        struct dentry *temp;
        int namelen = 0;
        char *full_path;
+       char dirsep = CIFS_DIR_SEP(CIFS_SB(direntry->d_sb));
 
        if(direntry == NULL)
                return NULL;  /* not much we can do if dentry is freed and
@@ -74,7 +75,7 @@ cifs_bp_rename_retry:
                if (namelen < 0) {
                        break;
                } else {
-                       full_path[namelen] = '\\';
+                       full_path[namelen] = dirsep;
                        strncpy(full_path + namelen + 1, temp->d_name.name,
                                temp->d_name.len);
                        cFYI(0, (" name: %s ", full_path + namelen));
@@ -183,6 +184,13 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
                         desiredAccess, CREATE_NOT_DIR,
                         &fileHandle, &oplock, buf, cifs_sb->local_nls,
                         cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+       if(rc == -EIO) {
+               /* old server, retry the open legacy style */
+               rc = SMBLegacyOpen(xid, pTcon, full_path, disposition,
+                       desiredAccess, CREATE_NOT_DIR,
+                       &fileHandle, &oplock, buf, cifs_sb->local_nls,
+                       cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+       } 
        if (rc) {
                cFYI(1, ("cifs_create returned 0x%x ", rc));
        } else {
@@ -208,7 +216,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
                                                CIFS_MOUNT_MAP_SPECIAL_CHR);
                        }
                else {
-                       /* BB implement via Windows security descriptors */
+                       /* BB implement mode setting via Windows security descriptors */
                        /* eg CIFSSMBWinSetPerms(xid,pTcon,full_path,mode,-1,-1,local_nls);*/
                        /* could set r/o dos attribute if mode & 0222 == 0 */
                }
@@ -225,10 +233,14 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
                }
 
                if (rc != 0) {
-                       cFYI(1,("Create worked but get_inode_info failed with rc = %d",
+                       cFYI(1,
+                            ("Create worked but get_inode_info failed rc = %d",
                              rc));
                } else {
-                       direntry->d_op = &cifs_dentry_ops;
+                       if (pTcon->nocase)
+                               direntry->d_op = &cifs_ci_dentry_ops;
+                       else
+                               direntry->d_op = &cifs_dentry_ops;
                        d_instantiate(direntry, newinode);
                }
                if((nd->flags & LOOKUP_OPEN) == FALSE) {
@@ -302,8 +314,7 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode, dev_t dev
        up(&direntry->d_sb->s_vfs_rename_sem);
        if(full_path == NULL)
                rc = -ENOMEM;
-       
-       if (full_path && (pTcon->ses->capabilities & CAP_UNIX)) {
+       else if (pTcon->ses->capabilities & CAP_UNIX) {
                if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
                        rc = CIFSSMBUnixSetPerms(xid, pTcon, full_path,
                                mode,(__u64)current->euid,(__u64)current->egid,
@@ -321,10 +332,49 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode, dev_t dev
                if(!rc) {
                        rc = cifs_get_inode_info_unix(&newinode, full_path,
                                                inode->i_sb,xid);
-                       direntry->d_op = &cifs_dentry_ops;
+                       if (pTcon->nocase)
+                               direntry->d_op = &cifs_ci_dentry_ops;
+                       else
+                               direntry->d_op = &cifs_dentry_ops;
                        if(rc == 0)
                                d_instantiate(direntry, newinode);
                }
+       } else {
+               if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
+                       int oplock = 0;
+                       u16 fileHandle;
+                       FILE_ALL_INFO * buf;
+
+                       cFYI(1,("sfu compat create special file"));
+
+                       buf = kmalloc(sizeof(FILE_ALL_INFO),GFP_KERNEL);
+                       if(buf == NULL) {
+                               kfree(full_path);
+                               FreeXid(xid);
+                               return -ENOMEM;
+                       }
+
+                       rc = CIFSSMBOpen(xid, pTcon, full_path,
+                                        FILE_CREATE, /* fail if exists */
+                                        GENERIC_WRITE /* BB would 
+                                         WRITE_OWNER | WRITE_DAC be better? */,
+                                        /* Create a file and set the
+                                           file attribute to SYSTEM */
+                                        CREATE_NOT_DIR | CREATE_OPTION_SPECIAL,
+                                        &fileHandle, &oplock, buf,
+                                        cifs_sb->local_nls,
+                                        cifs_sb->mnt_cifs_flags & 
+                                           CIFS_MOUNT_MAP_SPECIAL_CHR);
+
+                       if(!rc) {
+                               /* BB Do not bother to decode buf since no
+                                  local inode yet to put timestamps in */
+                               CIFSSMBClose(xid, pTcon, fileHandle);
+                               d_drop(direntry);
+                       }
+                       kfree(buf);
+                       /* add code here to set EAs */
+               }
        }
 
        kfree(full_path);
@@ -381,7 +431,10 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, struct name
                                         parent_dir_inode->i_sb,xid);
 
        if ((rc == 0) && (newInode != NULL)) {
-               direntry->d_op = &cifs_dentry_ops;
+               if (pTcon->nocase)
+                       direntry->d_op = &cifs_ci_dentry_ops;
+               else
+                       direntry->d_op = &cifs_dentry_ops;
                d_add(direntry, newInode);
 
                /* since paths are not looked up by component - the parent directories are presumed to be good here */
@@ -440,3 +493,42 @@ struct dentry_operations cifs_dentry_ops = {
 /* d_delete:       cifs_d_delete,       *//* not needed except for debugging */
        /* no need for d_hash, d_compare, d_release, d_iput ... yet. BB confirm this BB */
 };
+
+static int cifs_ci_hash(struct dentry *dentry, struct qstr *q)
+{
+       struct nls_table *codepage = CIFS_SB(dentry->d_inode->i_sb)->local_nls;
+       unsigned long hash;
+       int i;
+
+       hash = init_name_hash();
+       for (i = 0; i < q->len; i++)
+               hash = partial_name_hash(nls_tolower(codepage, q->name[i]),
+                                        hash);
+       q->hash = end_name_hash(hash);
+
+       return 0;
+}
+
+static int cifs_ci_compare(struct dentry *dentry, struct qstr *a,
+                          struct qstr *b)
+{
+       struct nls_table *codepage = CIFS_SB(dentry->d_inode->i_sb)->local_nls;
+
+       if ((a->len == b->len) &&
+           (nls_strnicmp(codepage, a->name, b->name, a->len) == 0)) {
+               /*
+                * To preserve case, don't let an existing negative dentry's
+                * case take precedence.  If a is not a negative dentry, this
+                * should have no side effects
+                */
+               memcpy((unsigned char *)a->name, b->name, a->len);
+               return 0;
+       }
+       return 1;
+}
+
+struct dentry_operations cifs_ci_dentry_ops = {
+       .d_revalidate = cifs_d_revalidate,
+       .d_hash = cifs_ci_hash,
+       .d_compare = cifs_ci_compare,
+};
index 7d2a9202c39a3291969307c1c57bdc18237fca57..a7a47bb36bf30888ea87bd62592ed732bba05d25 100644 (file)
@@ -78,6 +78,10 @@ int cifs_dir_notify(struct file * file, unsigned long arg)
        __u32 filter = FILE_NOTIFY_CHANGE_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES;
        __u16 netfid;
 
+
+       if(experimEnabled == 0)
+               return 0;
+
        xid = GetXid();
        cifs_sb = CIFS_SB(file->f_dentry->d_sb);
        pTcon = cifs_sb->tcon;
@@ -100,8 +104,10 @@ int cifs_dir_notify(struct file * file, unsigned long arg)
                } else {
                        filter = convert_to_cifs_notify_flags(arg);
                        if(filter != 0) {
-                               rc = CIFSSMBNotify(xid, pTcon, 0 /* no subdirs */, netfid, 
-                                       filter, cifs_sb->local_nls);
+                               rc = CIFSSMBNotify(xid, pTcon, 
+                                       0 /* no subdirs */, netfid,
+                                       filter, file, arg & DN_MULTISHOT,
+                                       cifs_sb->local_nls);
                        } else {
                                rc = -EINVAL;
                        }
@@ -109,7 +115,7 @@ int cifs_dir_notify(struct file * file, unsigned long arg)
                        it would close automatically but may be a way
                        to do it easily when inode freed or when
                        notify info is cleared/changed */
-            cERROR(1,("notify rc %d",rc));
+                       cFYI(1,("notify rc %d",rc));
                }
        }
        
index 3497125189dfde1a8b19506216954495fd061c7b..da4f5e10b3cc0f84c922660e89745bb14631cff1 100644 (file)
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 #include <linux/fs.h>
+#include <linux/backing-dev.h>
 #include <linux/stat.h>
 #include <linux/fcntl.h>
+#include <linux/mpage.h>
 #include <linux/pagemap.h>
 #include <linux/pagevec.h>
 #include <linux/smp_lock.h>
+#include <linux/writeback.h>
+#include <linux/delay.h>
 #include <asm/div64.h>
 #include "cifsfs.h"
 #include "cifspdu.h"
@@ -47,6 +51,11 @@ static inline struct cifsFileInfo *cifs_init_private(
        private_data->pInode = inode;
        private_data->invalidHandle = FALSE;
        private_data->closePend = FALSE;
+       /* we have to track num writers to the inode, since writepages
+       does not tell us which handle the write is for so there can
+       be a close (overlapping with write) of the filehandle that
+       cifs_writepages chose to use */
+       atomic_set(&private_data->wrtPending,0); 
 
        return private_data;
 }
@@ -256,6 +265,13 @@ int cifs_open(struct inode *inode, struct file *file)
                         CREATE_NOT_DIR, &netfid, &oplock, buf,
                         cifs_sb->local_nls, cifs_sb->mnt_cifs_flags
                                 & CIFS_MOUNT_MAP_SPECIAL_CHR);
+       if (rc == -EIO) {
+               /* Old server, try legacy style OpenX */
+               rc = SMBLegacyOpen(xid, pTcon, full_path, disposition,
+                       desiredAccess, CREATE_NOT_DIR, &netfid, &oplock, buf,
+                       cifs_sb->local_nls, cifs_sb->mnt_cifs_flags
+                               & CIFS_MOUNT_MAP_SPECIAL_CHR);
+       }
        if (rc) {
                cFYI(1, ("cifs_open returned 0x%x ", rc));
                goto out;
@@ -463,6 +479,20 @@ int cifs_close(struct inode *inode, struct file *file)
                        /* no sense reconnecting to close a file that is
                           already closed */
                        if (pTcon->tidStatus != CifsNeedReconnect) {
+                               int timeout = 2;
+                               while((atomic_read(&pSMBFile->wrtPending) != 0)
+                                        && (timeout < 1000) ) {
+                                       /* Give write a better chance to get to
+                                       server ahead of the close.  We do not
+                                       want to add a wait_q here as it would
+                                       increase the memory utilization as
+                                       the struct would be in each open file,
+                                       but this should give enough time to 
+                                       clear the socket */
+                                       cERROR(1,("close with pending writes"));
+                                       msleep(timeout);
+                                       timeout *= 4;
+                               } 
                                write_unlock(&file->f_owner.lock);
                                rc = CIFSSMBClose(xid, pTcon,
                                                  pSMBFile->netfid);
@@ -744,14 +774,7 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data,
                                    15 seconds is plenty */
        }
 
-#ifdef CONFIG_CIFS_STATS
-       if (total_written > 0) {
-               atomic_inc(&pTcon->num_writes);
-               spin_lock(&pTcon->stat_lock);
-               pTcon->bytes_written += total_written;
-               spin_unlock(&pTcon->stat_lock);
-       }
-#endif         
+       cifs_stats_bytes_written(pTcon, total_written);
 
        /* since the write may have blocked check these pointers again */
        if (file->f_dentry) {
@@ -791,9 +814,8 @@ static ssize_t cifs_write(struct file *file, const char *write_data,
 
        pTcon = cifs_sb->tcon;
 
-       /* cFYI(1,
-          (" write %d bytes to offset %lld of %s", write_size,
-          *poffset, file->f_dentry->d_name.name)); */
+       cFYI(1,("write %zd bytes to offset %lld of %s", write_size,
+          *poffset, file->f_dentry->d_name.name));
 
        if (file->private_data == NULL)
                return -EBADF;
@@ -846,7 +868,26 @@ static ssize_t cifs_write(struct file *file, const char *write_data,
                                if (rc != 0)
                                        break;
                        }
-
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+                       /* BB FIXME We can not sign across two buffers yet */
+                       if((experimEnabled) && ((pTcon->ses->server->secMode & 
+                        (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) == 0)) {
+                               struct kvec iov[2];
+                               unsigned int len;
+
+                               len = min((size_t)cifs_sb->wsize,
+                                         write_size - total_written);
+                               /* iov[0] is reserved for smb header */
+                               iov[1].iov_base = (char *)write_data +
+                                                 total_written;
+                               iov[1].iov_len = len;
+                               rc = CIFSSMBWrite2(xid, pTcon,
+                                               open_file->netfid, len,
+                                               *poffset, &bytes_written,
+                                               iov, 1, long_op);
+                       } else
+                       /* BB FIXME fixup indentation of line below */
+#endif                 
                        rc = CIFSSMBWrite(xid, pTcon,
                                 open_file->netfid,
                                 min_t(const int, cifs_sb->wsize, 
@@ -867,14 +908,7 @@ static ssize_t cifs_write(struct file *file, const char *write_data,
                                    15 seconds is plenty */
        }
 
-#ifdef CONFIG_CIFS_STATS
-       if (total_written > 0) {
-               atomic_inc(&pTcon->num_writes);
-               spin_lock(&pTcon->stat_lock);
-               pTcon->bytes_written += total_written;
-               spin_unlock(&pTcon->stat_lock);
-       }
-#endif         
+       cifs_stats_bytes_written(pTcon, total_written);
 
        /* since the write may have blocked check these pointers again */
        if (file->f_dentry) {
@@ -893,6 +927,43 @@ static ssize_t cifs_write(struct file *file, const char *write_data,
        return total_written;
 }
 
+struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode)
+{
+       struct cifsFileInfo *open_file;
+       int rc;
+
+       read_lock(&GlobalSMBSeslock);
+       list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
+               if (open_file->closePend)
+                       continue;
+               if (open_file->pfile &&
+                   ((open_file->pfile->f_flags & O_RDWR) ||
+                    (open_file->pfile->f_flags & O_WRONLY))) {
+                       atomic_inc(&open_file->wrtPending);
+                       read_unlock(&GlobalSMBSeslock);
+                       if((open_file->invalidHandle) && 
+                          (!open_file->closePend) /* BB fixme -since the second clause can not be true remove it BB */) {
+                               rc = cifs_reopen_file(&cifs_inode->vfs_inode, 
+                                                     open_file->pfile, FALSE);
+                               /* if it fails, try another handle - might be */
+                               /* dangerous to hold up writepages with retry */
+                               if(rc) {
+                                       cFYI(1,("failed on reopen file in wp"));
+                                       read_lock(&GlobalSMBSeslock);
+                                       /* can not use this handle, no write
+                                       pending on this one after all */
+                                       atomic_dec
+                                            (&open_file->wrtPending);
+                                       continue;
+                               }
+                       }
+                       return open_file;
+               }
+       }
+       read_unlock(&GlobalSMBSeslock);
+       return NULL;
+}
+
 static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
 {
        struct address_space *mapping = page->mapping;
@@ -903,10 +974,7 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
        struct cifs_sb_info *cifs_sb;
        struct cifsTconInfo *pTcon;
        struct inode *inode;
-       struct cifsInodeInfo *cifsInode;
-       struct cifsFileInfo *open_file = NULL;
-       struct list_head *tmp;
-       struct list_head *tmp1;
+       struct cifsFileInfo *open_file;
 
        if (!mapping || !mapping->host)
                return -EFAULT;
@@ -934,49 +1002,20 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
        if (mapping->host->i_size - offset < (loff_t)to)
                to = (unsigned)(mapping->host->i_size - offset); 
 
-       cifsInode = CIFS_I(mapping->host);
-       read_lock(&GlobalSMBSeslock); 
-       /* BB we should start at the end */
-       list_for_each_safe(tmp, tmp1, &cifsInode->openFileList) {            
-               open_file = list_entry(tmp, struct cifsFileInfo, flist);
-               if (open_file->closePend)
-                       continue;
-               /* We check if file is open for writing first */
-               if ((open_file->pfile) && 
-                  ((open_file->pfile->f_flags & O_RDWR) || 
-                       (open_file->pfile->f_flags & O_WRONLY))) {
-                       read_unlock(&GlobalSMBSeslock);
-                       bytes_written = cifs_write(open_file->pfile,
-                                               write_data, to-from,
-                                               &offset);
-                       read_lock(&GlobalSMBSeslock);
+       open_file = find_writable_file(CIFS_I(mapping->host));
+       if (open_file) {
+               bytes_written = cifs_write(open_file->pfile, write_data,
+                                          to-from, &offset);
+               atomic_dec(&open_file->wrtPending);
                /* Does mm or vfs already set times? */
-                       inode->i_atime = 
-                       inode->i_mtime = current_fs_time(inode->i_sb);
-                       if ((bytes_written > 0) && (offset)) {
-                               rc = 0;
-                       } else if (bytes_written < 0) {
-                               if (rc == -EBADF) {
-                               /* have seen a case in which kernel seemed to
-                                  have closed/freed a file even with writes
-                                  active so we might as well see if there are
-                                  other file structs to try for the same
-                                  inode before giving up */
-                                       continue;
-                               } else
-                                       rc = bytes_written;
-                       }
-                       break;  /* now that we found a valid file handle and
-                                  tried to write to it we are done, no sense
-                                  continuing to loop looking for another */
-               }
-               if (tmp->next == NULL) {
-                       cFYI(1, ("File instance %p removed", tmp));
-                       break;
+               inode->i_atime = inode->i_mtime = current_fs_time(inode->i_sb);
+               if ((bytes_written > 0) && (offset)) {
+                       rc = 0;
+               } else if (bytes_written < 0) {
+                       if (rc != -EBADF)
+                               rc = bytes_written;
                }
-       }
-       read_unlock(&GlobalSMBSeslock);
-       if (open_file == NULL) {
+       } else {
                cFYI(1, ("No writeable filehandles for inode"));
                rc = -EIO;
        }
@@ -985,20 +1024,207 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
        return rc;
 }
 
-#if 0
+#ifdef CONFIG_CIFS_EXPERIMENTAL
 static int cifs_writepages(struct address_space *mapping,
-       struct writeback_control *wbc)
+                          struct writeback_control *wbc)
 {
-       int rc = -EFAULT;
+       struct backing_dev_info *bdi = mapping->backing_dev_info;
+       unsigned int bytes_to_write;
+       unsigned int bytes_written;
+       struct cifs_sb_info *cifs_sb;
+       int done = 0;
+       pgoff_t end = -1;
+       pgoff_t index;
+       int is_range = 0;
+       struct kvec iov[32];
+       int len;
+       int n_iov = 0;
+       pgoff_t next;
+       int nr_pages;
+       __u64 offset = 0;
+       struct cifsFileInfo *open_file;
+       struct page *page;
+       struct pagevec pvec;
+       int rc = 0;
+       int scanned = 0;
        int xid;
 
+       cifs_sb = CIFS_SB(mapping->host->i_sb);
+       
+       /*
+        * If wsize is smaller that the page cache size, default to writing
+        * one page at a time via cifs_writepage
+        */
+       if (cifs_sb->wsize < PAGE_CACHE_SIZE)
+               return generic_writepages(mapping, wbc);
+
+       /* BB FIXME we do not have code to sign across multiple buffers yet,
+          so go to older writepage style write which we can sign if needed */
+       if((cifs_sb->tcon->ses) && (cifs_sb->tcon->ses->server))
+               if(cifs_sb->tcon->ses->server->secMode &
+                          (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
+                       return generic_writepages(mapping, wbc);
+
+       /*
+        * BB: Is this meaningful for a non-block-device file system?
+        * If it is, we should test it again after we do I/O
+        */
+       if (wbc->nonblocking && bdi_write_congested(bdi)) {
+               wbc->encountered_congestion = 1;
+               return 0;
+       }
+
        xid = GetXid();
 
-       /* Find contiguous pages then iterate through repeating
-          call 16K write then Setpageuptodate or if LARGE_WRITE_X
-          support then send larger writes via kevec so as to eliminate
-          a memcpy */
+       pagevec_init(&pvec, 0);
+       if (wbc->sync_mode == WB_SYNC_NONE)
+               index = mapping->writeback_index; /* Start from prev offset */
+       else {
+               index = 0;
+               scanned = 1;
+       }
+       if (wbc->start || wbc->end) {
+               index = wbc->start >> PAGE_CACHE_SHIFT;
+               end = wbc->end >> PAGE_CACHE_SHIFT;
+               is_range = 1;
+               scanned = 1;
+       }
+retry:
+       while (!done && (index <= end) &&
+              (nr_pages = pagevec_lookup_tag(&pvec, mapping, &index,
+                       PAGECACHE_TAG_DIRTY,
+                       min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1))) {
+               int first;
+               unsigned int i;
+
+               first = -1;
+               next = 0;
+               n_iov = 0;
+               bytes_to_write = 0;
+
+               for (i = 0; i < nr_pages; i++) {
+                       page = pvec.pages[i];
+                       /*
+                        * At this point we hold neither mapping->tree_lock nor
+                        * lock on the page itself: the page may be truncated or
+                        * invalidated (changing page->mapping to NULL), or even
+                        * swizzled back from swapper_space to tmpfs file
+                        * mapping
+                        */
+
+                       if (first < 0)
+                               lock_page(page);
+                       else if (TestSetPageLocked(page))
+                               break;
+
+                       if (unlikely(page->mapping != mapping)) {
+                               unlock_page(page);
+                               break;
+                       }
+
+                       if (unlikely(is_range) && (page->index > end)) {
+                               done = 1;
+                               unlock_page(page);
+                               break;
+                       }
+
+                       if (next && (page->index != next)) {
+                               /* Not next consecutive page */
+                               unlock_page(page);
+                               break;
+                       }
+
+                       if (wbc->sync_mode != WB_SYNC_NONE)
+                               wait_on_page_writeback(page);
+
+                       if (PageWriteback(page) ||
+                                       !test_clear_page_dirty(page)) {
+                               unlock_page(page);
+                               break;
+                       }
+
+                       if (page_offset(page) >= mapping->host->i_size) {
+                               done = 1;
+                               unlock_page(page);
+                               break;
+                       }
+
+                       /*
+                        * BB can we get rid of this?  pages are held by pvec
+                        */
+                       page_cache_get(page);
+
+                       len = min(mapping->host->i_size - page_offset(page),
+                                 (loff_t)PAGE_CACHE_SIZE);
+
+                       /* reserve iov[0] for the smb header */
+                       n_iov++;
+                       iov[n_iov].iov_base = kmap(page);
+                       iov[n_iov].iov_len = len;
+                       bytes_to_write += len;
+
+                       if (first < 0) {
+                               first = i;
+                               offset = page_offset(page);
+                       }
+                       next = page->index + 1;
+                       if (bytes_to_write + PAGE_CACHE_SIZE > cifs_sb->wsize)
+                               break;
+               }
+               if (n_iov) {
+                       /* Search for a writable handle every time we call
+                        * CIFSSMBWrite2.  We can't rely on the last handle
+                        * we used to still be valid
+                        */
+                       open_file = find_writable_file(CIFS_I(mapping->host));
+                       if (!open_file) {
+                               cERROR(1, ("No writable handles for inode"));
+                               rc = -EBADF;
+                       } else {
+                               rc = CIFSSMBWrite2(xid, cifs_sb->tcon,
+                                                  open_file->netfid,
+                                                  bytes_to_write, offset,
+                                                  &bytes_written, iov, n_iov,
+                                                  1);
+                               atomic_dec(&open_file->wrtPending);
+                               if (rc || bytes_written < bytes_to_write) {
+                                       cERROR(1,("Write2 ret %d, written = %d",
+                                                 rc, bytes_written));
+                                       /* BB what if continued retry is
+                                          requested via mount flags? */
+                                       set_bit(AS_EIO, &mapping->flags);
+                                       SetPageError(page);
+                               } else {
+                                       cifs_stats_bytes_written(cifs_sb->tcon,
+                                                                bytes_written);
+                               }
+                       }
+                       for (i = 0; i < n_iov; i++) {
+                               page = pvec.pages[first + i];
+                               kunmap(page);
+                               unlock_page(page);
+                               page_cache_release(page);
+                       }
+                       if ((wbc->nr_to_write -= n_iov) <= 0)
+                               done = 1;
+                       index = next;
+               }
+               pagevec_release(&pvec);
+       }
+       if (!scanned && !done) {
+               /*
+                * We hit the last page and there is more work to be done: wrap
+                * back to the start of the file
+                */
+               scanned = 1;
+               index = 0;
+               goto retry;
+       }
+       if (!is_range)
+               mapping->writeback_index = index;
+
        FreeXid(xid);
+
        return rc;
 }
 #endif
@@ -1207,12 +1433,10 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data,
                                if (rc != 0)
                                        break;
                        }
-
                        rc = CIFSSMBRead(xid, pTcon,
-                                open_file->netfid,
-                                current_read_size, *poffset,
-                                &bytes_read, &smb_read_data);
-
+                                       open_file->netfid,
+                                       current_read_size, *poffset,
+                                       &bytes_read, &smb_read_data);
                        pSMBr = (struct smb_com_read_rsp *)smb_read_data;
                        if (copy_to_user(current_offset, 
                                         smb_read_data + 4 /* RFC1001 hdr */
@@ -1235,12 +1459,7 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data,
                                return rc;
                        }
                } else {
-#ifdef CONFIG_CIFS_STATS
-                       atomic_inc(&pTcon->num_reads);
-                       spin_lock(&pTcon->stat_lock);
-                       pTcon->bytes_read += total_read;
-                       spin_unlock(&pTcon->stat_lock);
-#endif
+                       cifs_stats_bytes_read(pTcon, bytes_read);
                        *poffset += bytes_read;
                }
        }
@@ -1280,6 +1499,13 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
             total_read += bytes_read, current_offset += bytes_read) {
                current_read_size = min_t(const int, read_size - total_read,
                                          cifs_sb->rsize);
+               /* For windows me and 9x we do not want to request more
+               than it negotiated since it will refuse the read then */
+               if((pTcon->ses) && 
+                       !(pTcon->ses->capabilities & CAP_LARGE_FILES)) {
+                       current_read_size = min_t(const int, current_read_size,
+                                       pTcon->ses->server->maxBuf - 128);
+               }
                rc = -EAGAIN;
                while (rc == -EAGAIN) {
                        if ((open_file->invalidHandle) && 
@@ -1289,11 +1515,10 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
                                if (rc != 0)
                                        break;
                        }
-
                        rc = CIFSSMBRead(xid, pTcon,
-                                open_file->netfid,
-                                current_read_size, *poffset,
-                                &bytes_read, &current_offset);
+                                       open_file->netfid,
+                                       current_read_size, *poffset,
+                                       &bytes_read, &current_offset);
                }
                if (rc || (bytes_read == 0)) {
                        if (total_read) {
@@ -1303,12 +1528,7 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
                                return rc;
                        }
                } else {
-#ifdef CONFIG_CIFS_STATS
-                       atomic_inc(&pTcon->num_reads);
-                       spin_lock(&pTcon->stat_lock);
-                       pTcon->bytes_read += total_read;
-                       spin_unlock(&pTcon->stat_lock);
-#endif
+                       cifs_stats_bytes_read(pTcon, total_read);
                        *poffset += bytes_read;
                }
        }
@@ -1452,10 +1672,11 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
                        }
 
                        rc = CIFSSMBRead(xid, pTcon,
-                               open_file->netfid,
-                               read_size, offset,
-                               &bytes_read, &smb_read_data);
-                       /* BB need to check return code here */
+                                       open_file->netfid,
+                                       read_size, offset,
+                                       &bytes_read, &smb_read_data);
+
+                       /* BB more RC checks ? */
                        if (rc== -EAGAIN) {
                                if (smb_read_data) {
                                        cifs_buf_release(smb_read_data);
@@ -1480,12 +1701,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
                                le16_to_cpu(pSMBr->DataOffset), &lru_pvec);
 
                        i +=  bytes_read >> PAGE_CACHE_SHIFT;
-#ifdef CONFIG_CIFS_STATS
-                       atomic_inc(&pTcon->num_reads);
-                       spin_lock(&pTcon->stat_lock);
-                       pTcon->bytes_read += bytes_read;
-                       spin_unlock(&pTcon->stat_lock);
-#endif
+                       cifs_stats_bytes_read(pTcon, bytes_read);
                        if ((int)(bytes_read & PAGE_CACHE_MASK) != bytes_read) {
                                i++; /* account for partial page */
 
@@ -1603,40 +1819,21 @@ static int cifs_readpage(struct file *file, struct page *page)
    page caching in the current Linux kernel design */
 int is_size_safe_to_change(struct cifsInodeInfo *cifsInode)
 {
-       struct list_head *tmp;
-       struct list_head *tmp1;
        struct cifsFileInfo *open_file = NULL;
-       int rc = TRUE;
 
-       if (cifsInode == NULL)
-               return rc;
-
-       read_lock(&GlobalSMBSeslock); 
-       list_for_each_safe(tmp, tmp1, &cifsInode->openFileList) {            
-               open_file = list_entry(tmp, struct cifsFileInfo, flist);
-               if (open_file == NULL)
-                       break;
-               if (open_file->closePend)
-                       continue;
-       /* We check if file is open for writing,   
-          BB we could supplement this with a check to see if file size
-          changes have been flushed to server - ie inode metadata dirty */
-               if ((open_file->pfile) && 
-                   ((open_file->pfile->f_flags & O_RDWR) || 
-                   (open_file->pfile->f_flags & O_WRONLY))) {
-                       rc = FALSE;
-                       break;
-               }
-               if (tmp->next == NULL) {
-                       cFYI(1, ("File instance %p removed", tmp));
-                       break;
-               }
-       }
-       read_unlock(&GlobalSMBSeslock);
-       return rc;
+       if (cifsInode)
+               open_file =  find_writable_file(cifsInode);
+       if(open_file) {
+               /* there is not actually a write pending so let
+               this handle go free and allow it to
+               be closable if needed */
+               atomic_dec(&open_file->wrtPending);
+               return 0;
+       } else
+               return 1;
 }
 
-
 static int cifs_prepare_write(struct file *file, struct page *page,
        unsigned from, unsigned to)
 {
@@ -1676,6 +1873,9 @@ struct address_space_operations cifs_addr_ops = {
        .readpage = cifs_readpage,
        .readpages = cifs_readpages,
        .writepage = cifs_writepage,
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+       .writepages = cifs_writepages,
+#endif
        .prepare_write = cifs_prepare_write,
        .commit_write = cifs_commit_write,
        .set_page_dirty = __set_page_dirty_nobuffers,
index 8d336a90025584a6fb524001ef71c123b6d881ec..912d401600f6e2f7abce605c63e115372b9ecdaa 100644 (file)
@@ -166,7 +166,13 @@ int cifs_get_inode_info_unix(struct inode **pinode,
                                inode->i_fop = &cifs_file_direct_ops;
                        else
                                inode->i_fop = &cifs_file_ops;
+                       if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
+                               inode->i_fop->lock = NULL;
                        inode->i_data.a_ops = &cifs_addr_ops;
+                       /* check if server can support readpages */
+                       if(pTcon->ses->server->maxBuf < 
+                           4096 + MAX_CIFS_HDR_SIZE)
+                               inode->i_data.a_ops->readpages = NULL;
                } else if (S_ISDIR(inode->i_mode)) {
                        cFYI(1, (" Directory inode"));
                        inode->i_op = &cifs_dir_inode_ops;
@@ -213,8 +219,18 @@ int cifs_get_inode_info(struct inode **pinode,
                pfindData = (FILE_ALL_INFO *)buf;
                /* could do find first instead but this returns more info */
                rc = CIFSSMBQPathInfo(xid, pTcon, search_path, pfindData,
-                             cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & 
+                             cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
                                CIFS_MOUNT_MAP_SPECIAL_CHR);
+               /* BB optimize code so we do not make the above call
+               when server claims no NT SMB support and the above call
+               failed at least once - set flag in tcon or mount */
+               if((rc == -EOPNOTSUPP) || (rc == -EINVAL)) {
+                       rc = SMBQueryInformation(xid, pTcon, search_path,
+                                       pfindData, cifs_sb->local_nls, 
+                                       cifs_sb->mnt_cifs_flags &
+                                         CIFS_MOUNT_MAP_SPECIAL_CHR);
+               }
+               
        }
        /* dump_mem("\nQPathInfo return data",&findData, sizeof(findData)); */
        if (rc) {
@@ -320,6 +336,16 @@ int cifs_get_inode_info(struct inode **pinode,
                   on dirs */
                        inode->i_mode = cifs_sb->mnt_dir_mode;
                        inode->i_mode |= S_IFDIR;
+               } else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) &&
+                          (cifsInfo->cifsAttrs & ATTR_SYSTEM) &&
+                          /* No need to le64 convert size of zero */
+                          (pfindData->EndOfFile == 0)) {
+                       inode->i_mode = cifs_sb->mnt_file_mode;
+                       inode->i_mode |= S_IFIFO;
+/* BB Finish for SFU style symlinks and devies */
+/*             } else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) &&
+                          (cifsInfo->cifsAttrs & ATTR_SYSTEM) && ) */
+
                } else {
                        inode->i_mode |= S_IFREG;
                        /* treat the dos attribute of read-only as read-only
@@ -359,7 +385,12 @@ int cifs_get_inode_info(struct inode **pinode,
                                inode->i_fop = &cifs_file_direct_ops;
                        else
                                inode->i_fop = &cifs_file_ops;
+                       if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
+                               inode->i_fop->lock = NULL;
                        inode->i_data.a_ops = &cifs_addr_ops;
+                       if(pTcon->ses->server->maxBuf < 
+                            4096 + MAX_CIFS_HDR_SIZE)
+                               inode->i_data.a_ops->readpages = NULL;
                } else if (S_ISDIR(inode->i_mode)) {
                        cFYI(1, (" Directory inode "));
                        inode->i_op = &cifs_dir_inode_ops;
@@ -577,7 +608,10 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)
                        rc = cifs_get_inode_info(&newinode, full_path, NULL,
                                                 inode->i_sb,xid);
 
-               direntry->d_op = &cifs_dentry_ops;
+               if (pTcon->nocase)
+                       direntry->d_op = &cifs_ci_dentry_ops;
+               else
+                       direntry->d_op = &cifs_dentry_ops;
                d_instantiate(direntry, newinode);
                if (direntry->d_inode)
                        direntry->d_inode->i_nlink = 2;
@@ -928,7 +962,6 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs)
        struct cifsTconInfo *pTcon;
        char *full_path = NULL;
        int rc = -EACCES;
-       int found = FALSE;
        struct cifsFileInfo *open_file = NULL;
        FILE_BASIC_INFO time_buf;
        int set_time = FALSE;
@@ -936,7 +969,6 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs)
        __u64 uid = 0xFFFFFFFFFFFFFFFFULL;
        __u64 gid = 0xFFFFFFFFFFFFFFFFULL;
        struct cifsInodeInfo *cifsInode;
-       struct list_head *tmp;
 
        xid = GetXid();
 
@@ -961,7 +993,6 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs)
        filemap_fdatawait(direntry->d_inode->i_mapping);
 
        if (attrs->ia_valid & ATTR_SIZE) {
-               read_lock(&GlobalSMBSeslock);
                /* To avoid spurious oplock breaks from server, in the case of
                   inodes that we already have open, avoid doing path based
                   setting of file size if we can do it by handle.
@@ -969,40 +1000,23 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs)
                   when the local oplock break takes longer to flush
                   writebehind data than the SMB timeout for the SetPathInfo
                   request would allow */
-               list_for_each(tmp, &cifsInode->openFileList) {
-                       open_file = list_entry(tmp, struct cifsFileInfo,
-                                              flist);
-                       /* We check if file is open for writing first */
-                       if ((open_file->pfile) &&
-                           ((open_file->pfile->f_flags & O_RDWR) ||
-                           (open_file->pfile->f_flags & O_WRONLY))) {
-                               if (open_file->invalidHandle == FALSE) {
-                                       /* we found a valid, writeable network
-                                          file handle to use to try to set the
-                                          file size */
-                                       __u16 nfid = open_file->netfid;
-                                       __u32 npid = open_file->pid;
-                                       read_unlock(&GlobalSMBSeslock);
-                                       found = TRUE;
-                                       rc = CIFSSMBSetFileSize(xid, pTcon,
-                                               attrs->ia_size, nfid, npid,
-                                               FALSE);
-                                       cFYI(1, ("SetFileSize by handle "
-                                                "(setattrs) rc = %d", rc));
-                                       /* Do not need reopen and retry on
-                                          EAGAIN since we will retry by
-                                          pathname below */
-
-                                       /* now that we found one valid file
-                                          handle no sense continuing to loop
-                                          trying others, so break here */
-                                       break;
-                               }
+               open_file = find_writable_file(cifsInode);
+               if (open_file) {
+                       __u16 nfid = open_file->netfid;
+                       __u32 npid = open_file->pid;
+                       rc = CIFSSMBSetFileSize(xid, pTcon, attrs->ia_size,
+                                               nfid, npid, FALSE);
+                       atomic_dec(&open_file->wrtPending);
+                       cFYI(1,("SetFSize for attrs rc = %d", rc));
+                       if(rc == -EINVAL) {
+                               int bytes_written;
+                               rc = CIFSSMBWrite(xid, pTcon,
+                                                 nfid, 0, attrs->ia_size,
+                                                 &bytes_written, NULL, NULL,
+                                                 1 /* 45 seconds */);
+                               cFYI(1,("Wrt seteof rc %d", rc));
                        }
                }
-               if (found == FALSE)
-                       read_unlock(&GlobalSMBSeslock);
-
                if (rc != 0) {
                        /* Set file size by pathname rather than by handle
                           either because no valid, writeable file handle for
@@ -1013,7 +1027,30 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs)
                                           cifs_sb->local_nls, 
                                           cifs_sb->mnt_cifs_flags &
                                                CIFS_MOUNT_MAP_SPECIAL_CHR);
-                       cFYI(1, (" SetEOF by path (setattrs) rc = %d", rc));
+                       cFYI(1, ("SetEOF by path (setattrs) rc = %d", rc));
+                       if(rc == -EINVAL) {
+                               __u16 netfid;
+                               int oplock = FALSE;
+
+                               rc = SMBLegacyOpen(xid, pTcon, full_path,
+                                       FILE_OPEN,
+                                       SYNCHRONIZE | FILE_WRITE_ATTRIBUTES,
+                                       CREATE_NOT_DIR, &netfid, &oplock,
+                                       NULL, cifs_sb->local_nls,
+                                       cifs_sb->mnt_cifs_flags &
+                                               CIFS_MOUNT_MAP_SPECIAL_CHR);
+                               if (rc==0) {
+                                       int bytes_written;
+                                       rc = CIFSSMBWrite(xid, pTcon,
+                                                       netfid, 0,
+                                                       attrs->ia_size,
+                                                       &bytes_written, NULL,
+                                                       NULL, 1 /* 45 sec */);
+                                       cFYI(1,("wrt seteof rc %d",rc));
+                                       CIFSSMBClose(xid, pTcon, netfid);
+                               }
+
+                       }
                }
 
                /* Server is ok setting allocation size implicitly - no need
@@ -1026,24 +1063,22 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs)
                        rc = vmtruncate(direntry->d_inode, attrs->ia_size);
                        cifs_truncate_page(direntry->d_inode->i_mapping,
                                           direntry->d_inode->i_size);
-               }
+               } else 
+                       goto cifs_setattr_exit;
        }
        if (attrs->ia_valid & ATTR_UID) {
-               cFYI(1, (" CIFS - UID changed to %d", attrs->ia_uid));
+               cFYI(1, ("UID changed to %d", attrs->ia_uid));
                uid = attrs->ia_uid;
-               /* entry->uid = cpu_to_le16(attr->ia_uid); */
        }
        if (attrs->ia_valid & ATTR_GID) {
-               cFYI(1, (" CIFS - GID changed to %d", attrs->ia_gid));
+               cFYI(1, ("GID changed to %d", attrs->ia_gid));
                gid = attrs->ia_gid;
-               /* entry->gid = cpu_to_le16(attr->ia_gid); */
        }
 
        time_buf.Attributes = 0;
        if (attrs->ia_valid & ATTR_MODE) {
-               cFYI(1, (" CIFS - Mode changed to 0x%x", attrs->ia_mode));
+               cFYI(1, ("Mode changed to 0x%x", attrs->ia_mode));
                mode = attrs->ia_mode;
-               /* entry->mode = cpu_to_le16(attr->ia_mode); */
        }
 
        if ((cifs_sb->tcon->ses->capabilities & CAP_UNIX)
@@ -1083,18 +1118,24 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs)
                    cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_mtime));
        } else
                time_buf.LastWriteTime = 0;
-
-       if (attrs->ia_valid & ATTR_CTIME) {
+       /* Do not set ctime explicitly unless other time
+          stamps are changed explicitly (i.e. by utime()
+          since we would then have a mix of client and
+          server times */
+          
+       if (set_time && (attrs->ia_valid & ATTR_CTIME)) {
                set_time = TRUE;
-               cFYI(1, (" CIFS - CTIME changed ")); /* BB probably no need */
+               /* Although Samba throws this field away
+               it may be useful to Windows - but we do
+               not want to set ctime unless some other
+               timestamp is changing */
+               cFYI(1, ("CIFS - CTIME changed "));
                time_buf.ChangeTime =
                    cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_ctime));
        } else
                time_buf.ChangeTime = 0;
 
        if (set_time || time_buf.Attributes) {
-               /* BB what if setting one attribute fails (such as size) but
-                  time setting works? */
                time_buf.CreationTime = 0;      /* do not change */
                /* In the future we should experiment - try setting timestamps
                   via Handle (SetFileInfo) instead of by path */
@@ -1133,12 +1174,21 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs)
                                        &time_buf, cifs_sb->local_nls); */
                        }
                }
+               /* Even if error on time set, no sense failing the call if
+               the server would set the time to a reasonable value anyway,
+               and this check ensures that we are not being called from
+               sys_utimes in which case we ought to fail the call back to
+               the user when the server rejects the call */
+               if((rc) && (attrs->ia_valid &&
+                        (ATTR_MODE | ATTR_GID | ATTR_UID | ATTR_SIZE)))
+                       rc = 0;
        }
 
        /* do not need local check to inode_check_ok since the server does
           that */
        if (!rc)
                rc = inode_setattr(direntry->d_inode, attrs);
+cifs_setattr_exit:
        kfree(full_path);
        FreeXid(xid);
        return rc;
index ab925ef4f863c9696b9ed0a1832e9d3d6a0c0d07..b43e071fe1106446fc2e48fc05299a7b8fabce4a 100644 (file)
@@ -198,7 +198,10 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname)
                             ("Create symlink worked but get_inode_info failed with rc = %d ",
                              rc));
                } else {
-                       direntry->d_op = &cifs_dentry_ops;
+                       if (pTcon->nocase)
+                               direntry->d_op = &cifs_ci_dentry_ops;
+                       else
+                               direntry->d_op = &cifs_dentry_ops;
                        d_instantiate(direntry, newinode);
                }
        }
index 20ae4153f791673d4137bf885db2b8b92cda8852..eba1de917f2a87c2d9d5a173978c26f4e10e06b2 100644 (file)
@@ -34,8 +34,6 @@ extern mempool_t *cifs_sm_req_poolp;
 extern mempool_t *cifs_req_poolp;
 extern struct task_struct * oplockThread;
 
-static __u16 GlobalMid;                /* multiplex id - rotating counter */
-
 /* The xid serves as a useful identifier for each incoming vfs request, 
    in a similar way to the mid which is useful to track each sent smb, 
    and CurrentXid can also provide a running counter (although it 
@@ -51,6 +49,8 @@ _GetXid(void)
        GlobalTotalActiveXid++;
        if (GlobalTotalActiveXid > GlobalMaxActiveXid)
                GlobalMaxActiveXid = GlobalTotalActiveXid;      /* keep high water mark for number of simultaneous vfs ops in our filesystem */
+       if(GlobalTotalActiveXid > 65000)
+               cFYI(1,("warning: more than 65000 requests active"));
        xid = GlobalCurrentXid++;
        spin_unlock(&GlobalMid_Lock);
        return xid;
@@ -218,6 +218,76 @@ cifs_small_buf_release(void *buf_to_free)
        return;
 }
 
+/* 
+       Find a free multiplex id (SMB mid). Otherwise there could be
+       mid collisions which might cause problems, demultiplexing the
+       wrong response to this request. Multiplex ids could collide if
+       one of a series requests takes much longer than the others, or
+       if a very large number of long lived requests (byte range
+       locks or FindNotify requests) are pending.  No more than
+       64K-1 requests can be outstanding at one time.  If no 
+       mids are available, return zero.  A future optimization
+       could make the combination of mids and uid the key we use
+       to demultiplex on (rather than mid alone).  
+       In addition to the above check, the cifs demultiplex
+       code already used the command code as a secondary
+       check of the frame and if signing is negotiated the
+       response would be discarded if the mid were the same
+       but the signature was wrong.  Since the mid is not put in the
+       pending queue until later (when it is about to be dispatched)
+       we do have to limit the number of outstanding requests 
+       to somewhat less than 64K-1 although it is hard to imagine
+       so many threads being in the vfs at one time.
+*/
+__u16 GetNextMid(struct TCP_Server_Info *server)
+{
+       __u16 mid = 0;
+       __u16 last_mid;
+       int   collision;  
+
+       if(server == NULL)
+               return mid;
+
+       spin_lock(&GlobalMid_Lock);
+       last_mid = server->CurrentMid; /* we do not want to loop forever */
+       server->CurrentMid++;
+       /* This nested loop looks more expensive than it is.
+       In practice the list of pending requests is short, 
+       fewer than 50, and the mids are likely to be unique
+       on the first pass through the loop unless some request
+       takes longer than the 64 thousand requests before it
+       (and it would also have to have been a request that
+        did not time out) */
+       while(server->CurrentMid != last_mid) {
+               struct list_head *tmp;
+               struct mid_q_entry *mid_entry;
+
+               collision = 0;
+               if(server->CurrentMid == 0)
+                       server->CurrentMid++;
+
+               list_for_each(tmp, &server->pending_mid_q) {
+                       mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
+
+                       if ((mid_entry->mid == server->CurrentMid) &&
+                           (mid_entry->midState == MID_REQUEST_SUBMITTED)) {
+                               /* This mid is in use, try a different one */
+                               collision = 1;
+                               break;
+                       }
+               }
+               if(collision == 0) {
+                       mid = server->CurrentMid;
+                       break;
+               }
+               server->CurrentMid++;
+       }
+       spin_unlock(&GlobalMid_Lock);
+       return mid;
+}
+
+/* NB: MID can not be set if treeCon not passed in, in that
+   case it is responsbility of caller to set the mid */
 void
 header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
                const struct cifsTconInfo *treeCon, int word_count
@@ -233,7 +303,8 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
            (2 * word_count) + sizeof (struct smb_hdr) -
            4 /*  RFC 1001 length field does not count */  +
            2 /* for bcc field itself */ ;
-       /* Note that this is the only network field that has to be converted to big endian and it is done just before we send it */
+       /* Note that this is the only network field that has to be converted
+          to big endian and it is done just before we send it */
 
        buffer->Protocol[0] = 0xFF;
        buffer->Protocol[1] = 'S';
@@ -245,8 +316,6 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
        buffer->Pid = cpu_to_le16((__u16)current->tgid);
        buffer->PidHigh = cpu_to_le16((__u16)(current->tgid >> 16));
        spin_lock(&GlobalMid_Lock);
-       GlobalMid++;
-       buffer->Mid = GlobalMid;
        spin_unlock(&GlobalMid_Lock);
        if (treeCon) {
                buffer->Tid = treeCon->tid;
@@ -256,8 +325,9 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
                        if (treeCon->ses->capabilities & CAP_STATUS32) {
                                buffer->Flags2 |= SMBFLG2_ERR_STATUS;
                        }
-
-                       buffer->Uid = treeCon->ses->Suid;       /* always in LE format */
+                       /* Uid is not converted */
+                       buffer->Uid = treeCon->ses->Suid;
+                       buffer->Mid = GetNextMid(treeCon->ses->server);
                        if(multiuser_mount != 0) {
                /* For the multiuser case, there are few obvious technically  */
                /* possible mechanisms to match the local linux user (uid)    */
@@ -305,6 +375,8 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
                }
                if (treeCon->Flags & SMB_SHARE_IS_IN_DFS)
                        buffer->Flags2 |= SMBFLG2_DFS;
+               if (treeCon->nocase)
+                       buffer->Flags  |= SMBFLG_CASELESS;
                if((treeCon->ses) && (treeCon->ses->server))
                        if(treeCon->ses->server->secMode & 
                          (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
@@ -347,7 +419,8 @@ checkSMBhdr(struct smb_hdr *smb, __u16 mid)
 int
 checkSMB(struct smb_hdr *smb, __u16 mid, int length)
 {
-       __u32 len = be32_to_cpu(smb->smb_buf_length);
+       __u32 len = smb->smb_buf_length;
+       __u32 clc_len;  /* calculated length */
        cFYI(0,
             ("Entering checkSMB with Length: %x, smb_buf_length: %x ",
              length, len));
@@ -368,23 +441,29 @@ checkSMB(struct smb_hdr *smb, __u16 mid, int length)
                        cERROR(1,
                               ("smb_buf_length greater than MaxBufSize"));
                cERROR(1,
-                      ("bad smb detected. Illegal length. The mid=%d",
+                      ("bad smb detected. Illegal length. mid=%d",
                        smb->Mid));
                return 1;
        }
 
        if (checkSMBhdr(smb, mid))
                return 1;
-
-       if ((4 + len != smbCalcSize(smb))
+       clc_len = smbCalcSize_LE(smb);
+       if ((4 + len != clc_len)
            || (4 + len != (unsigned int)length)) {
-               return 0;
-       } else {
-               cERROR(1, ("smbCalcSize %x ", smbCalcSize(smb)));
-               cERROR(1,
-                      ("bad smb size detected. The Mid=%d", smb->Mid));
-               return 1;
+               cERROR(1, ("Calculated size 0x%x vs actual length 0x%x",
+                               clc_len, 4 + len));
+               cERROR(1, ("bad smb size detected for Mid=%d", smb->Mid));
+               /* Windows XP can return a few bytes too much, presumably
+               an illegal pad, at the end of byte range lock responses 
+               so we allow for up to eight byte pad, as long as actual
+               received length is as long or longer than calculated length */
+               if((4+len > clc_len) && (len <= clc_len + 3))
+                       return 0;
+               else
+                       return 1;
        }
+       return 0;
 }
 int
 is_valid_oplock_break(struct smb_hdr *buf)
@@ -448,9 +527,7 @@ is_valid_oplock_break(struct smb_hdr *buf)
        list_for_each(tmp, &GlobalTreeConnectionList) {
                tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
                if (tcon->tid == buf->Tid) {
-#ifdef CONFIG_CIFS_STATS
-                       atomic_inc(&tcon->num_oplock_brks);
-#endif
+                       cifs_stats_inc(&tcon->num_oplock_brks);
                        list_for_each(tmp1,&tcon->openFileList){
                                netfile = list_entry(tmp1,struct cifsFileInfo,
                                                     tlist);
@@ -603,6 +680,7 @@ cifsConvertToUCS(__le16 * target, const char *source, int maxlen,
        int i,j,charlen;
        int len_remaining = maxlen;
        char src_char;
+       __u16 temp;
 
        if(!mapChars) 
                return cifs_strtoUCS((wchar_t *) target, source, PATH_MAX, cp);
@@ -639,13 +717,14 @@ cifsConvertToUCS(__le16 * target, const char *source, int maxlen,
                                break;*/
                        default:
                                charlen = cp->char2uni(source+i,
-                                       len_remaining, target+j);
+                                       len_remaining, &temp);
                                /* if no match, use question mark, which
                                at least in some cases servers as wild card */
                                if(charlen < 1) {
                                        target[j] = cpu_to_le16(0x003f);
                                        charlen = 1;
-                               }
+                               } else
+                                       target[j] = cpu_to_le16(temp);
                                len_remaining -= charlen;
                                /* character may take more than one byte in the
                                   the source string, but will take exactly two
index a92af41d44119a2c464f3b905b181f4445c9f1db..f7814689844b2f4440b374024f92d7899349b29b 100644 (file)
@@ -133,7 +133,6 @@ static const struct smb_to_posix_error mapping_table_ERRHRD[] = {
 int
 cifs_inet_pton(int address_family, char *cp,void *dst)
 {
-       struct in_addr address;
        int value;
        int digit;
        int i;
@@ -190,8 +189,7 @@ cifs_inet_pton(int address_family, char *cp,void *dst)
        if (value > addr_class_max[end - bytes])
                return 0;
 
-       address.s_addr = *((__be32 *) bytes) | htonl(value);
-       *((__be32 *)dst) = address.s_addr;
+       *((__be32 *)dst) = *((__be32 *) bytes) | htonl(value);
        return 1; /* success */
 }
 
@@ -815,7 +813,7 @@ map_smb_to_linux_error(struct smb_hdr *smb)
        if (smb->Flags2 & SMBFLG2_ERR_STATUS) {
                /* translate the newer STATUS codes to old style errors and then to POSIX errors */
                __u32 err = le32_to_cpu(smb->Status.CifsError);
-               if(cifsFYI)
+               if(cifsFYI & CIFS_RC)
                        cifs_print_status(err);
                ntstatus_to_dos(err, &smberrclass, &smberrcode);
        } else {
@@ -870,7 +868,14 @@ unsigned int
 smbCalcSize(struct smb_hdr *ptr)
 {
        return (sizeof (struct smb_hdr) + (2 * ptr->WordCount) +
-               BCC(ptr));
+               2 /* size of the bcc field */ + BCC(ptr));
+}
+
+unsigned int
+smbCalcSize_LE(struct smb_hdr *ptr)
+{
+       return (sizeof (struct smb_hdr) + (2 * ptr->WordCount) +
+               2 /* size of the bcc field */ + le16_to_cpu(BCC_LE(ptr)));
 }
 
 /* The following are taken from fs/ntfs/util.c */
index 6facb41117a3e85637fcff5abc4d815c0bcc5efc..803389b64a2c7c465656b3986bab452ba93d651e 100644 (file)
@@ -19,8 +19,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
  */
 
-#pragma pack(1)
-
 #define NTLMSSP_SIGNATURE "NTLMSSP"
 /* Message Types */
 #define NtLmNegotiate     cpu_to_le32(1)
@@ -63,7 +61,7 @@ typedef struct _SECURITY_BUFFER {
        __le16 Length;
        __le16 MaximumLength;
        __le32 Buffer;          /* offset to buffer */
-} SECURITY_BUFFER;
+} __attribute__((packed)) SECURITY_BUFFER;
 
 typedef struct _NEGOTIATE_MESSAGE {
        __u8 Signature[sizeof (NTLMSSP_SIGNATURE)];
@@ -73,7 +71,7 @@ typedef struct _NEGOTIATE_MESSAGE {
        SECURITY_BUFFER WorkstationName;        /* RFC 1001 and ASCII */
        char DomainString[0];
        /* followed by WorkstationString */
-} NEGOTIATE_MESSAGE, *PNEGOTIATE_MESSAGE;
+} __attribute__((packed)) NEGOTIATE_MESSAGE, *PNEGOTIATE_MESSAGE;
 
 typedef struct _CHALLENGE_MESSAGE {
        __u8 Signature[sizeof (NTLMSSP_SIGNATURE)];
@@ -83,7 +81,7 @@ typedef struct _CHALLENGE_MESSAGE {
        __u8 Challenge[CIFS_CRYPTO_KEY_SIZE];
        __u8 Reserved[8];
        SECURITY_BUFFER TargetInfoArray;
-} CHALLENGE_MESSAGE, *PCHALLENGE_MESSAGE;
+} __attribute__((packed)) CHALLENGE_MESSAGE, *PCHALLENGE_MESSAGE;
 
 typedef struct _AUTHENTICATE_MESSAGE {
        __u8 Signature[sizeof (NTLMSSP_SIGNATURE)];
@@ -96,6 +94,4 @@ typedef struct _AUTHENTICATE_MESSAGE {
        SECURITY_BUFFER SessionKey;
        __le32 NegotiateFlags;
        char UserString[0];
-} AUTHENTICATE_MESSAGE, *PAUTHENTICATE_MESSAGE;
-
-#pragma pack()                 /* resume default structure packing */
+} __attribute__((packed)) AUTHENTICATE_MESSAGE, *PAUTHENTICATE_MESSAGE;
index 22557716f9afb48c032d9c7759706183f01b6a9f..a86bd1c076021bb88bec0da95a4d939b0785d69d 100644 (file)
@@ -91,7 +91,10 @@ static int construct_dentry(struct qstr *qstring, struct file *file,
                }
 
                *ptmp_inode = new_inode(file->f_dentry->d_sb);
-               tmp_dentry->d_op = &cifs_dentry_ops;
+               if (pTcon->nocase)
+                       tmp_dentry->d_op = &cifs_ci_dentry_ops;
+               else
+                       tmp_dentry->d_op = &cifs_dentry_ops;
                if(*ptmp_inode == NULL)
                        return rc;
                rc = 1;
@@ -148,6 +151,13 @@ static void fill_in_inode(struct inode *tmp_inode,
                        tmp_inode->i_mode = cifs_sb->mnt_dir_mode;
                }
                tmp_inode->i_mode |= S_IFDIR;
+       } else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) && 
+                  (attr & ATTR_SYSTEM) && (end_of_file == 0)) {
+               *pobject_type = DT_FIFO;
+               tmp_inode->i_mode |= S_IFIFO;
+/* BB Finish for SFU style symlinks and devies */
+/*     } else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) &&
+               (attr & ATTR_SYSTEM) && ) { */
 /* we no longer mark these because we could not follow them */
 /*        } else if (attr & ATTR_REPARSE) {
                 *pobject_type = DT_LNK;
@@ -187,11 +197,17 @@ static void fill_in_inode(struct inode *tmp_inode,
                        tmp_inode->i_fop = &cifs_file_direct_ops;
                else
                        tmp_inode->i_fop = &cifs_file_ops;
+               if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
+                       tmp_inode->i_fop->lock = NULL;
                tmp_inode->i_data.a_ops = &cifs_addr_ops;
-
+               if((cifs_sb->tcon) && (cifs_sb->tcon->ses) &&
+                  (cifs_sb->tcon->ses->server->maxBuf <
+                       4096 + MAX_CIFS_HDR_SIZE))
+                       tmp_inode->i_data.a_ops->readpages = NULL;
                if(isNewInode)
-                       return; /* No sense invalidating pages for new inode since we
-                                          have not started caching readahead file data yet */
+                       return; /* No sense invalidating pages for new inode
+                                  since have not started caching readahead file
+                                  data yet */
 
                if (timespec_equal(&tmp_inode->i_mtime, &local_mtime) &&
                        (local_size == tmp_inode->i_size)) {
@@ -290,7 +306,13 @@ static void unix_fill_in_inode(struct inode *tmp_inode,
                        tmp_inode->i_fop = &cifs_file_direct_ops;
                else
                        tmp_inode->i_fop = &cifs_file_ops;
+               if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
+                       tmp_inode->i_fop->lock = NULL;
                tmp_inode->i_data.a_ops = &cifs_addr_ops;
+               if((cifs_sb->tcon) && (cifs_sb->tcon->ses) &&
+                  (cifs_sb->tcon->ses->server->maxBuf < 
+                       4096 + MAX_CIFS_HDR_SIZE))
+                       tmp_inode->i_data.a_ops->readpages = NULL;
 
                if(isNewInode)
                        return; /* No sense invalidating pages for new inode since we
@@ -374,7 +396,8 @@ ffirst_retry:
 
        rc = CIFSFindFirst(xid, pTcon,full_path,cifs_sb->local_nls,
                &cifsFile->netfid, &cifsFile->srch_inf,
-               cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+               cifs_sb->mnt_cifs_flags & 
+                       CIFS_MOUNT_MAP_SPECIAL_CHR, CIFS_DIR_SEP(cifs_sb));
        if(rc == 0)
                cifsFile->invalidHandle = FALSE;
        if((rc == -EOPNOTSUPP) && 
@@ -491,6 +514,30 @@ static int cifs_entry_is_dot(char *current_entry, struct cifsFileInfo *cfile)
        return rc;
 }
 
+/* Check if directory that we are searching has changed so we can decide
+   whether we can use the cached search results from the previous search */
+static int is_dir_changed(struct file * file)
+{
+       struct inode * inode;
+       struct cifsInodeInfo *cifsInfo;
+
+       if(file->f_dentry == NULL)
+               return 0;
+
+       inode = file->f_dentry->d_inode;
+
+       if(inode == NULL)
+               return 0;
+
+       cifsInfo = CIFS_I(inode);
+
+       if(cifsInfo->time == 0)
+               return 1; /* directory was changed, perhaps due to unlink */
+       else
+               return 0;
+
+}
+
 /* find the corresponding entry in the search */
 /* Note that the SMB server returns search entries for . and .. which
    complicates logic here if we choose to parse for them and we do not
@@ -507,7 +554,8 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon,
        struct cifsFileInfo * cifsFile = file->private_data;
        /* check if index in the buffer */
        
-       if((cifsFile == NULL) || (ppCurrentEntry == NULL) || (num_to_ret == NULL))
+       if((cifsFile == NULL) || (ppCurrentEntry == NULL) || 
+          (num_to_ret == NULL))
                return -ENOENT;
        
        *ppCurrentEntry = NULL;
@@ -515,7 +563,9 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon,
                cifsFile->srch_inf.index_of_last_entry - 
                        cifsFile->srch_inf.entries_in_buffer;
 /*     dump_cifs_file_struct(file, "In fce ");*/
-       if(index_to_find < first_entry_in_buffer) {
+       if(((index_to_find < cifsFile->srch_inf.index_of_last_entry) && 
+            is_dir_changed(file)) || 
+          (index_to_find < first_entry_in_buffer)) {
                /* close and restart search */
                cFYI(1,("search backing up - close and restart search"));
                cifsFile->invalidHandle = TRUE;
@@ -536,7 +586,8 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon,
        while((index_to_find >= cifsFile->srch_inf.index_of_last_entry) && 
              (rc == 0) && (cifsFile->srch_inf.endOfSearch == FALSE)){
                cFYI(1,("calling findnext2"));
-               rc = CIFSFindNext(xid,pTcon,cifsFile->netfid, &cifsFile->srch_inf);
+               rc = CIFSFindNext(xid,pTcon,cifsFile->netfid, 
+                                 &cifsFile->srch_inf);
                if(rc)
                        return -ENOENT;
        }
@@ -548,14 +599,13 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon,
                char * end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + 
                        smbCalcSize((struct smb_hdr *)
                                cifsFile->srch_inf.ntwrk_buf_start);
-/*     dump_cifs_file_struct(file,"found entry in fce "); */
                first_entry_in_buffer = cifsFile->srch_inf.index_of_last_entry
                                        - cifsFile->srch_inf.entries_in_buffer;
                pos_in_buf = index_to_find - first_entry_in_buffer;
                cFYI(1,("found entry - pos_in_buf %d",pos_in_buf)); 
                current_entry = cifsFile->srch_inf.srch_entries_start;
                for(i=0;(i<(pos_in_buf)) && (current_entry != NULL);i++) {
-                       /* go entry to next entry figuring out which we need to start with */
+                       /* go entry by entry figuring out which is first */
                        /* if( . or ..)
                                skip */
                        rc = cifs_entry_is_dot(current_entry,cifsFile);
@@ -582,11 +632,10 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon,
        }
 
        if(pos_in_buf >= cifsFile->srch_inf.entries_in_buffer) {
-               cFYI(1,("can not return entries when pos_in_buf beyond last entry"));
+               cFYI(1,("can not return entries pos_in_buf beyond last entry"));
                *num_to_ret = 0;
        } else
                *num_to_ret = cifsFile->srch_inf.entries_in_buffer - pos_in_buf;
-/*     dump_cifs_file_struct(file, "end fce ");*/
 
        return rc;
 }
@@ -721,7 +770,8 @@ static int cifs_filldir(char *pfindEntry, struct file *file,
                              (FILE_DIRECTORY_INFO *)pfindEntry,&obj_type, rc);
        }
        
-       rc = filldir(direntry,qstring.name,qstring.len,file->f_pos,tmp_inode->i_ino,obj_type);
+       rc = filldir(direntry,qstring.name,qstring.len,file->f_pos,
+                    tmp_inode->i_ino,obj_type);
        if(rc) {
                cFYI(1,("filldir rc = %d",rc));
        }
@@ -805,15 +855,12 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
                FreeXid(xid);
                return -EIO;
        }
-/*     dump_cifs_file_struct(file, "Begin rdir "); */
 
        cifs_sb = CIFS_SB(file->f_dentry->d_sb);
        pTcon = cifs_sb->tcon;
        if(pTcon == NULL)
                return -EINVAL;
 
-/*     cFYI(1,("readdir2 pos: %lld",file->f_pos)); */
-
        switch ((int) file->f_pos) {
        case 0:
                /*if (filldir(direntry, ".", 1, file->f_pos,
@@ -866,7 +913,6 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
                cifsFile->search_resume_name = NULL; */
 
                /* BB account for . and .. in f_pos as special case */
-               /* dump_cifs_file_struct(file, "rdir after default ");*/
 
                rc = find_cifs_entry(xid,pTcon, file,
                                &current_entry,&num_to_fill);
@@ -906,14 +952,14 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
                                cifs_save_resume_key(current_entry,cifsFile);
                                break;
                        } else 
-                               current_entry = nxt_dir_entry(current_entry,end_of_smb);
+                               current_entry = nxt_dir_entry(current_entry,
+                                                             end_of_smb);
                }
                kfree(tmp_buf);
                break;
        } /* end switch */
 
 rddir2_exit:
-       /* dump_cifs_file_struct(file, "end rdir ");  */
        FreeXid(xid);
        return rc;
 }
index 806c0ed06da9c9a77ecd6da3d7807de4f7d30a6d..9222033cad8ec34a5623b21402ca3998c394344a 100644 (file)
@@ -21,8 +21,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
  */
 
-#pragma pack(1)
-
 /* NB: unlike smb/cifs packets, the RFC1002 structures are big endian */
 
        /* RFC 1002 session packet types */
@@ -48,17 +46,17 @@ struct rfc1002_session_packet {
                        __u8 calling_len;
                        __u8 calling_name[32];
                        __u8 scope2; /* null */
-               } session_req;
+               } __attribute__((packed)) session_req;
                struct {
                        __u32 retarget_ip_addr;
                        __u16 port;
-               } retarget_resp;
+               } __attribute__((packed)) retarget_resp;
                __u8 neg_ses_resp_error_code;
                /* POSITIVE_SESSION_RESPONSE packet does not include trailer.
                SESSION_KEEP_ALIVE packet also does not include a trailer.
                Trailer for the SESSION_MESSAGE packet is SMB/CIFS header */
-       } trailer;
-};
+       } __attribute__((packed)) trailer;
+} __attribute__((packed));
 
 /* Negative Session Response error codes */
 #define RFC1002_NOT_LISTENING_CALLED  0x80 /* not listening on called name */
@@ -74,6 +72,3 @@ server netbios name). Currently server names are resolved only via DNS
 (tcp name) or ip address or an /etc/hosts equivalent mapping to ip address.*/
 
 #define DEFAULT_CIFS_CALLED_NAME  "*SMBSERVER      "
-
-#pragma pack()         /* resume default structure packing */
-                                                             
index 0046c219833d6cfbef77e85addc663e1be71e290..981ea0d8b9cdaa4b06f53f7fdae975b41a1bffa6 100644 (file)
@@ -49,7 +49,8 @@ AllocMidQEntry(struct smb_hdr *smb_buffer, struct cifsSesInfo *ses)
                return NULL;
        }
        
-       temp = (struct mid_q_entry *) mempool_alloc(cifs_mid_poolp,SLAB_KERNEL | SLAB_NOFS);
+       temp = (struct mid_q_entry *) mempool_alloc(cifs_mid_poolp,
+                                                   SLAB_KERNEL | SLAB_NOFS);
        if (temp == NULL)
                return temp;
        else {
@@ -58,7 +59,9 @@ AllocMidQEntry(struct smb_hdr *smb_buffer, struct cifsSesInfo *ses)
                temp->pid = current->pid;
                temp->command = smb_buffer->Command;
                cFYI(1, ("For smb_command %d", temp->command));
-               do_gettimeofday(&temp->when_sent);
+       /*      do_gettimeofday(&temp->when_sent);*/ /* easier to use jiffies */
+               /* when mid allocated can be before when sent */
+               temp->when_alloc = jiffies;
                temp->ses = ses;
                temp->tsk = current;
        }
@@ -74,6 +77,9 @@ AllocMidQEntry(struct smb_hdr *smb_buffer, struct cifsSesInfo *ses)
 static void
 DeleteMidQEntry(struct mid_q_entry *midEntry)
 {
+#ifdef CONFIG_CIFS_STATS2
+       unsigned long now;
+#endif
        spin_lock(&GlobalMid_Lock);
        midEntry->midState = MID_FREE;
        list_del(&midEntry->qhead);
@@ -83,6 +89,22 @@ DeleteMidQEntry(struct mid_q_entry *midEntry)
                cifs_buf_release(midEntry->resp_buf);
        else
                cifs_small_buf_release(midEntry->resp_buf);
+#ifdef CONFIG_CIFS_STATS2
+       now = jiffies;
+       /* commands taking longer than one second are indications that
+          something is wrong, unless it is quite a slow link or server */
+       if((now - midEntry->when_alloc) > HZ) {
+               if((cifsFYI & CIFS_TIMER) && 
+                  (midEntry->command != SMB_COM_LOCKING_ANDX)) {
+                       printk(KERN_DEBUG " CIFS slow rsp: cmd %d mid %d",
+                              midEntry->command, midEntry->mid);
+                       printk(" A: 0x%lx S: 0x%lx R: 0x%lx\n",
+                              now - midEntry->when_alloc,
+                              now - midEntry->when_sent,
+                              now - midEntry->when_received);
+               }
+       }
+#endif
        mempool_free(midEntry, cifs_mid_poolp);
 }
 
@@ -146,32 +168,37 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer,
           Flags2 is converted in SendReceive */
 
        smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length);
-       cFYI(1, ("Sending smb of length %d ", smb_buf_length));
+       cFYI(1, ("Sending smb of length %d", smb_buf_length));
        dump_smb(smb_buffer, len);
 
        while (len > 0) {
                rc = kernel_sendmsg(ssocket, &smb_msg, &iov, 1, len);
                if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
                        i++;
-                       if(i > 60) {
+               /* smaller timeout here than send2 since smaller size */
+               /* Although it may not be required, this also is smaller 
+                  oplock break time */  
+                       if(i > 12) {
                                cERROR(1,
-                                  ("sends on sock %p stuck for 30 seconds",
+                                  ("sends on sock %p stuck for 7 seconds",
                                    ssocket));
                                rc = -EAGAIN;
                                break;
                        }
-                       msleep(500);
+                       msleep(1 << i);
                        continue;
                }
                if (rc < 0) 
                        break;
+               else
+                       i = 0; /* reset i after each successful send */
                iov.iov_base += rc;
                iov.iov_len -= rc;
                len -= rc;
        }
 
        if (rc < 0) {
-               cERROR(1,("Error %d sending data on socket to server.", rc));
+               cERROR(1,("Error %d sending data on socket to server", rc));
        } else {
                rc = 0;
        }
@@ -179,26 +206,21 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer,
        return rc;
 }
 
-#ifdef CIFS_EXPERIMENTAL
-/* BB finish off this function, adding support for writing set of pages as iovec */
-/* and also adding support for operations that need to parse the response smb    */
-
-int
-smb_sendv(struct socket *ssocket, struct smb_hdr *smb_buffer,
-        unsigned int smb_buf_length, struct kvec * write_vector 
-         /* page list */, struct sockaddr *sin)
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+static int
+smb_send2(struct socket *ssocket, struct kvec *iov, int n_vec,
+         struct sockaddr *sin)
 {
        int rc = 0;
        int i = 0;
        struct msghdr smb_msg;
-       number_of_pages += 1; /* account for SMB header */
-       struct kvec * piov  = kmalloc(number_of_pages * sizeof(struct kvec));
-       unsigned len = smb_buf_length + 4;
-
+       struct smb_hdr *smb_buffer = iov[0].iov_base;
+       unsigned int len = iov[0].iov_len;
+       unsigned int total_len;
+       int first_vec = 0;
+       
        if(ssocket == NULL)
                return -ENOTSOCK; /* BB eventually add reconnect code here */
-       iov.iov_base = smb_buffer;
-       iov.iov_len = len;
 
        smb_msg.msg_name = sin;
        smb_msg.msg_namelen = sizeof (struct sockaddr);
@@ -211,49 +233,80 @@ smb_sendv(struct socket *ssocket, struct smb_hdr *smb_buffer,
           cifssmb.c and RFC1001 len is converted to bigendian in smb_send 
           Flags2 is converted in SendReceive */
 
+
+       total_len = 0;
+       for (i = 0; i < n_vec; i++)
+               total_len += iov[i].iov_len;
+
        smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length);
-       cFYI(1, ("Sending smb of length %d ", smb_buf_length));
+       cFYI(1, ("Sending smb:  total_len %d", total_len));
        dump_smb(smb_buffer, len);
 
-       while (len > 0) {
-               rc = kernel_sendmsg(ssocket, &smb_msg, &iov, number_of_pages, 
-                                   len);
+       while (total_len) {
+               rc = kernel_sendmsg(ssocket, &smb_msg, &iov[first_vec],
+                                   n_vec - first_vec, total_len);
                if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
                        i++;
-                       if(i > 60) {
+                       if(i >= 14) {
                                cERROR(1,
-                                  ("sends on sock %p stuck for 30 seconds",
+                                  ("sends on sock %p stuck for 15 seconds",
                                    ssocket));
                                rc = -EAGAIN;
                                break;
                        }
-                       msleep(500);
+                       msleep(1 << i);
                        continue;
                }
                if (rc < 0) 
                        break;
-               iov.iov_base += rc;
-               iov.iov_len -= rc;
-               len -= rc;
+
+               if (rc >= total_len) {
+                       WARN_ON(rc > total_len);
+                       break;
+               }
+               if(rc == 0) {
+                       /* should never happen, letting socket clear before
+                          retrying is our only obvious option here */
+                       cERROR(1,("tcp sent no data"));
+                       msleep(500);
+                       continue;
+               }
+               total_len -= rc;
+               /* the line below resets i */
+               for (i = first_vec; i < n_vec; i++) {
+                       if (iov[i].iov_len) {
+                               if (rc > iov[i].iov_len) {
+                                       rc -= iov[i].iov_len;
+                                       iov[i].iov_len = 0;
+                               } else {
+                                       iov[i].iov_base += rc;
+                                       iov[i].iov_len -= rc;
+                                       first_vec = i;
+                                       break;
+                               }
+                       }
+               }
+               i = 0; /* in case we get ENOSPC on the next send */
        }
 
        if (rc < 0) {
-               cERROR(1,("Error %d sending data on socket to server.", rc));
-       } else {
+               cERROR(1,("Error %d sending data on socket to server", rc));
+       } else
                rc = 0;
-       }
 
        return rc;
 }
 
-
 int
-CIFSSendRcv(const unsigned int xid, struct cifsSesInfo *ses,
-           struct smb_hdr *in_buf, struct kvec * write_vector /* page list */, int *pbytes_returned, const int long_op)
+SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, 
+            struct kvec *iov, int n_vec, int *pbytes_returned,
+            const int long_op)
 {
        int rc = 0;
-       unsigned long timeout = 15 * HZ;
-       struct mid_q_entry *midQ = NULL;
+       unsigned int receive_len;
+       unsigned long timeout;
+       struct mid_q_entry *midQ;
+       struct smb_hdr *in_buf = iov[0].iov_base;
 
        if (ses == NULL) {
                cERROR(1,("Null smb session"));
@@ -263,14 +316,8 @@ CIFSSendRcv(const unsigned int xid, struct cifsSesInfo *ses,
                cERROR(1,("Null tcp session"));
                return -EIO;
        }
-       if(pbytes_returned == NULL)
-               return -EIO;
-       else
-               *pbytes_returned = 0;
 
-  
-
-       if(ses->server->tcpStatus == CIFS_EXITING)
+       if(ses->server->tcpStatus == CifsExiting)
                return -ENOENT;
 
        /* Ensure that we do not send more than 50 overlapping requests 
@@ -282,11 +329,18 @@ CIFSSendRcv(const unsigned int xid, struct cifsSesInfo *ses,
        } else {
                spin_lock(&GlobalMid_Lock); 
                while(1) {        
-                       if(atomic_read(&ses->server->inFlight) >= cifs_max_pending){
+                       if(atomic_read(&ses->server->inFlight) >= 
+                                       cifs_max_pending){
                                spin_unlock(&GlobalMid_Lock);
+#ifdef CONFIG_CIFS_STATS2
+                               atomic_inc(&ses->server->num_waiters);
+#endif
                                wait_event(ses->server->request_q,
                                        atomic_read(&ses->server->inFlight)
                                         < cifs_max_pending);
+#ifdef CONFIG_CIFS_STATS2
+                               atomic_dec(&ses->server->num_waiters);
+#endif
                                spin_lock(&GlobalMid_Lock);
                        } else {
                                if(ses->server->tcpStatus == CifsExiting) {
@@ -314,17 +368,17 @@ CIFSSendRcv(const unsigned int xid, struct cifsSesInfo *ses,
 
        if (ses->server->tcpStatus == CifsExiting) {
                rc = -ENOENT;
-               goto cifs_out_label;
+               goto out_unlock2;
        } else if (ses->server->tcpStatus == CifsNeedReconnect) {
                cFYI(1,("tcp session dead - return to caller to retry"));
                rc = -EAGAIN;
-               goto cifs_out_label;
+               goto out_unlock2;
        } else if (ses->status != CifsGood) {
                /* check if SMB session is bad because we are setting it up */
                if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) && 
                        (in_buf->Command != SMB_COM_NEGOTIATE)) {
                        rc = -EAGAIN;
-                       goto cifs_out_label;
+                       goto out_unlock2;
                } /* else ok - we are setting up session */
        }
        midQ = AllocMidQEntry(in_buf, ses);
@@ -338,51 +392,162 @@ CIFSSendRcv(const unsigned int xid, struct cifsSesInfo *ses,
                return -ENOMEM;
        }
 
-       if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
-               up(&ses->server->tcpSem);
-               cERROR(1,
-                      ("Illegal length, greater than maximum frame, %d ",
-                       in_buf->smb_buf_length));
+/* BB FIXME */
+/*     rc = cifs_sign_smb2(iov, n_vec, ses->server, &midQ->sequence_number); */
+
+       midQ->midState = MID_REQUEST_SUBMITTED;
+#ifdef CONFIG_CIFS_STATS2
+       atomic_inc(&ses->server->inSend);
+#endif
+       rc = smb_send2(ses->server->ssocket, iov, n_vec,
+                     (struct sockaddr *) &(ses->server->addr.sockAddr));
+#ifdef CONFIG_CIFS_STATS2
+       atomic_dec(&ses->server->inSend);
+       midQ->when_sent = jiffies;
+#endif
+       if(rc < 0) {
                DeleteMidQEntry(midQ);
+               up(&ses->server->tcpSem);
                /* If not lock req, update # of requests on wire to server */
                if(long_op < 3) {
                        atomic_dec(&ses->server->inFlight); 
                        wake_up(&ses->server->request_q);
                }
-               return -EIO;
+               return rc;
+       } else
+               up(&ses->server->tcpSem);
+       if (long_op == -1)
+               goto cifs_no_response_exit2;
+       else if (long_op == 2) /* writes past end of file can take loong time */
+               timeout = 180 * HZ;
+       else if (long_op == 1)
+               timeout = 45 * HZ; /* should be greater than 
+                       servers oplock break timeout (about 43 seconds) */
+       else if (long_op > 2) {
+               timeout = MAX_SCHEDULE_TIMEOUT;
+       } else
+               timeout = 15 * HZ;
+       /* wait for 15 seconds or until woken up due to response arriving or 
+          due to last connection to this server being unmounted */
+       if (signal_pending(current)) {
+               /* if signal pending do not hold up user for full smb timeout
+               but we still give response a change to complete */
+               timeout = 2 * HZ;
+       }   
+
+       /* No user interrupts in wait - wreaks havoc with performance */
+       if(timeout != MAX_SCHEDULE_TIMEOUT) {
+               timeout += jiffies;
+               wait_event(ses->server->response_q,
+                       (!(midQ->midState & MID_REQUEST_SUBMITTED)) || 
+                       time_after(jiffies, timeout) || 
+                       ((ses->server->tcpStatus != CifsGood) &&
+                        (ses->server->tcpStatus != CifsNew)));
+       } else {
+               wait_event(ses->server->response_q,
+                       (!(midQ->midState & MID_REQUEST_SUBMITTED)) || 
+                       ((ses->server->tcpStatus != CifsGood) &&
+                        (ses->server->tcpStatus != CifsNew)));
        }
 
-       /* BB can we sign efficiently in this path? */
-       rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
+       spin_lock(&GlobalMid_Lock);
+       if (midQ->resp_buf) {
+               spin_unlock(&GlobalMid_Lock);
+               receive_len = midQ->resp_buf->smb_buf_length;
+       } else {
+               cERROR(1,("No response to cmd %d mid %d",
+                       midQ->command, midQ->mid));
+               if(midQ->midState == MID_REQUEST_SUBMITTED) {
+                       if(ses->server->tcpStatus == CifsExiting)
+                               rc = -EHOSTDOWN;
+                       else {
+                               ses->server->tcpStatus = CifsNeedReconnect;
+                               midQ->midState = MID_RETRY_NEEDED;
+                       }
+               }
 
-       midQ->midState = MID_REQUEST_SUBMITTED;
-/*     rc = smb_sendv(ses->server->ssocket, in_buf, in_buf->smb_buf_length,
-                      piovec, 
-                      (struct sockaddr *) &(ses->server->addr.sockAddr));*/
-       if(rc < 0) {
+               if (rc != -EHOSTDOWN) {
+                       if(midQ->midState == MID_RETRY_NEEDED) {
+                               rc = -EAGAIN;
+                               cFYI(1,("marking request for retry"));
+                       } else {
+                               rc = -EIO;
+                       }
+               }
+               spin_unlock(&GlobalMid_Lock);
                DeleteMidQEntry(midQ);
-               up(&ses->server->tcpSem);
                /* If not lock req, update # of requests on wire to server */
                if(long_op < 3) {
                        atomic_dec(&ses->server->inFlight); 
                        wake_up(&ses->server->request_q);
                }
                return rc;
-       } else
-               up(&ses->server->tcpSem);
-cifs_out_label:
-       if(midQ)
-               DeleteMidQEntry(midQ);
-                                                                                                                           
+       }
+  
+       if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
+               cERROR(1, ("Frame too large received.  Length: %d  Xid: %d",
+                       receive_len, xid));
+               rc = -EIO;
+       } else {                /* rcvd frame is ok */
+
+               if (midQ->resp_buf && 
+                       (midQ->midState == MID_RESPONSE_RECEIVED)) {
+                       in_buf->smb_buf_length = receive_len;
+                       /* BB verify that length would not overrun small buf */
+                       memcpy((char *)in_buf + 4,
+                              (char *)midQ->resp_buf + 4,
+                              receive_len);
+
+                       dump_smb(in_buf, 80);
+                       /* convert the length into a more usable form */
+                       if((receive_len > 24) &&
+                          (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
+                                       SECMODE_SIGN_ENABLED))) {
+                               rc = cifs_verify_signature(in_buf,
+                                               ses->server->mac_signing_key,
+                                               midQ->sequence_number+1);
+                               if(rc) {
+                                       cERROR(1,("Unexpected SMB signature"));
+                                       /* BB FIXME add code to kill session */
+                               }
+                       }
+
+                       *pbytes_returned = in_buf->smb_buf_length;
+
+                       /* BB special case reconnect tid and uid here? */
+                       rc = map_smb_to_linux_error(in_buf);
+
+                       /* convert ByteCount if necessary */
+                       if (receive_len >=
+                           sizeof (struct smb_hdr) -
+                           4 /* do not count RFC1001 header */  +
+                           (2 * in_buf->WordCount) + 2 /* bcc */ )
+                               BCC(in_buf) = le16_to_cpu(BCC(in_buf));
+               } else {
+                       rc = -EIO;
+                       cFYI(1,("Bad MID state?"));
+               }
+       }
+cifs_no_response_exit2:
+       DeleteMidQEntry(midQ);
+
        if(long_op < 3) {
-               atomic_dec(&ses->server->inFlight);
+               atomic_dec(&ses->server->inFlight); 
                wake_up(&ses->server->request_q);
        }
 
        return rc;
-}
 
+out_unlock2:
+       up(&ses->server->tcpSem);
+       /* If not lock req, update # of requests on wire to server */
+       if(long_op < 3) {
+               atomic_dec(&ses->server->inFlight); 
+               wake_up(&ses->server->request_q);
+       }
 
+       return rc;
+}
 #endif /* CIFS_EXPERIMENTAL */
 
 int
@@ -419,9 +584,15 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
                        if(atomic_read(&ses->server->inFlight) >= 
                                        cifs_max_pending){
                                spin_unlock(&GlobalMid_Lock);
+#ifdef CONFIG_CIFS_STATS2
+                               atomic_inc(&ses->server->num_waiters);
+#endif
                                wait_event(ses->server->request_q,
                                        atomic_read(&ses->server->inFlight)
                                         < cifs_max_pending);
+#ifdef CONFIG_CIFS_STATS2
+                               atomic_dec(&ses->server->num_waiters);
+#endif
                                spin_lock(&GlobalMid_Lock);
                        } else {
                                if(ses->server->tcpStatus == CifsExiting) {
@@ -490,8 +661,15 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
        rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
 
        midQ->midState = MID_REQUEST_SUBMITTED;
+#ifdef CONFIG_CIFS_STATS2
+       atomic_inc(&ses->server->inSend);
+#endif
        rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length,
                      (struct sockaddr *) &(ses->server->addr.sockAddr));
+#ifdef CONFIG_CIFS_STATS2
+       atomic_dec(&ses->server->inSend);
+       midQ->when_sent = jiffies;
+#endif
        if(rc < 0) {
                DeleteMidQEntry(midQ);
                up(&ses->server->tcpSem);
@@ -506,7 +684,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
        if (long_op == -1)
                goto cifs_no_response_exit;
        else if (long_op == 2) /* writes past end of file can take loong time */
-               timeout = 300 * HZ;
+               timeout = 180 * HZ;
        else if (long_op == 1)
                timeout = 45 * HZ; /* should be greater than 
                        servers oplock break timeout (about 43 seconds) */
@@ -540,9 +718,10 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
        spin_lock(&GlobalMid_Lock);
        if (midQ->resp_buf) {
                spin_unlock(&GlobalMid_Lock);
-               receive_len = be32_to_cpu(*(__be32 *)midQ->resp_buf);
+               receive_len = midQ->resp_buf->smb_buf_length;
        } else {
-               cERROR(1,("No response buffer"));
+               cERROR(1,("No response for cmd %d mid %d",
+                         midQ->command, midQ->mid));
                if(midQ->midState == MID_REQUEST_SUBMITTED) {
                        if(ses->server->tcpStatus == CifsExiting)
                                rc = -EHOSTDOWN;
@@ -610,7 +789,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
                                BCC(out_buf) = le16_to_cpu(BCC(out_buf));
                } else {
                        rc = -EIO;
-                       cFYI(1,("Bad MID state? "));
+                       cERROR(1,("Bad MID state? "));
                }
        }
 cifs_no_response_exit:
index ffab4783ac644a984b6c0ff7a529b43720e6bc53..c27f8d4098be3cd713b2934f6eefdc8d03bb4a68 100644 (file)
@@ -247,7 +247,7 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc)
        wait_queue_head_t *wqh;
 
        if (!atomic_read(&inode->i_count))
-               WARN_ON(!(inode->i_state & I_WILL_FREE));
+               WARN_ON(!(inode->i_state & (I_WILL_FREE|I_FREEING)));
        else
                WARN_ON(inode->i_state & I_WILL_FREE);
 
index de58579a1d0e77a84e4f7d5fcc63c8eadcbb494d..50a7749cfca1bc73c0331ca032db93adaf5f348f 100644 (file)
@@ -1,18 +1,15 @@
 ToDo/Notes:
        - Find and fix bugs.
-       - In between ntfs_prepare/commit_write, need exclusion between
-         simultaneous file extensions.  This is given to us by holding i_sem
-         on the inode.  The only places in the kernel when a file is resized
-         are prepare/commit write and truncate for both of which i_sem is
-         held.  Just have to be careful in readpage/writepage and all other
-         helpers not running under i_sem that we play nice...
-         Also need to be careful with initialized_size extention in
-         ntfs_prepare_write. Basically, just be _very_ careful in this code...
-         UPDATE: The only things that need to be checked are read/writepage
-         which do not hold i_sem.  Note writepage cannot change i_size but it
-         needs to cope with a concurrent i_size change, just like readpage.
-         Also both need to cope with concurrent changes to the other sizes,
-         i.e. initialized/allocated/compressed size, as well.
+       - The only places in the kernel where a file is resized are
+         ntfs_file_write*() and ntfs_truncate() for both of which i_sem is
+         held.  Just have to be careful in read-/writepage and other helpers
+         not running under i_sem that we play nice...  Also need to be careful
+         with initialized_size extension in ntfs_file_write*() and writepage.
+         UPDATE: The only things that need to be checked are the compressed
+         write and the other attribute resize/write cases like index
+         attributes, etc.  For now none of these are implemented so are safe.
+       - Implement filling in of holes in aops.c::ntfs_writepage() and its
+         helpers.
        - Implement mft.c::sync_mft_mirror_umount().  We currently will just
          leave the volume dirty on umount if the final iput(vol->mft_ino)
          causes a write of any mirrored mft records due to the mft mirror
@@ -22,6 +19,68 @@ ToDo/Notes:
        - Enable the code for setting the NT4 compatibility flag when we start
          making NTFS 1.2 specific modifications.
 
+2.1.25 - (Almost) fully implement write(2) and truncate(2).
+
+       - Change ntfs_map_runlist_nolock(), ntfs_attr_find_vcn_nolock() and
+         {__,}ntfs_cluster_free() to also take an optional attribute search
+         context as argument.  This allows calling these functions with the
+         mft record mapped.  Update all callers.
+       - Fix potential deadlock in ntfs_mft_data_extend_allocation_nolock()
+         error handling by passing in the active search context when calling
+         ntfs_cluster_free().
+       - Change ntfs_cluster_alloc() to take an extra boolean parameter
+         specifying whether the cluster are being allocated to extend an
+         attribute or to fill a hole.
+       - Change ntfs_attr_make_non_resident() to call ntfs_cluster_alloc()
+         with @is_extension set to TRUE and remove the runlist terminator
+         fixup code as this is now done by ntfs_cluster_alloc().
+       - Change ntfs_attr_make_non_resident to take the attribute value size
+         as an extra parameter.  This is needed since we need to know the size
+         before we can map the mft record and our callers always know it.  The
+         reason we cannot simply read the size from the vfs inode i_size is
+         that this is not necessarily uptodate.  This happens when
+         ntfs_attr_make_non_resident() is called in the ->truncate call path.
+       - Fix ntfs_attr_make_non_resident() to update the vfs inode i_blocks
+         which is zero for a resident attribute but should no longer be zero
+         once the attribute is non-resident as it then has real clusters
+         allocated.
+       - Add fs/ntfs/attrib.[hc]::ntfs_attr_extend_allocation(), a function to
+         extend the allocation of an attributes.  Optionally, the data size,
+         but not the initialized size can be extended, too.
+       - Implement fs/ntfs/inode.[hc]::ntfs_truncate().  It only supports
+         uncompressed and unencrypted files and it never creates sparse files
+         at least for the moment (making a file sparse requires us to modify
+         its directory entries and we do not support directory operations at
+         the moment).  Also, support for highly fragmented files, i.e. ones
+         whose data attribute is split across multiple extents, is severly
+         limited.  When such a case is encountered, EOPNOTSUPP is returned.
+       - Enable ATTR_SIZE attribute changes in ntfs_setattr().  This completes
+         the initial implementation of file truncation.  Now both open(2)ing
+         a file with the O_TRUNC flag and the {,f}truncate(2) system calls
+         will resize a file appropriately.  The limitations are that only
+         uncompressed and unencrypted files are supported.  Also, there is
+         only very limited support for highly fragmented files (the ones whose
+         $DATA attribute is split into multiple attribute extents).
+       - In attrib.c::ntfs_attr_set() call balance_dirty_pages_ratelimited()
+         and cond_resched() in the main loop as we could be dirtying a lot of
+         pages and this ensures we play nice with the VM and the system as a
+         whole.
+       - Implement file operations ->write, ->aio_write, ->writev for regular
+         files.  This replaces the old use of generic_file_write(), et al and
+         the address space operations ->prepare_write and ->commit_write.
+         This means that both sparse and non-sparse (unencrypted and
+         uncompressed) files can now be extended using the normal write(2)
+         code path.  There are two limitations at present and these are that
+         we never create sparse files and that we only have limited support
+         for highly fragmented files, i.e. ones whose data attribute is split
+         across multiple extents.   When such a case is encountered,
+         EOPNOTSUPP is returned.
+       - $EA attributes can be both resident and non-resident.
+       - Use %z for size_t to fix compilation warnings.  (Andrew Morton)
+       - Fix compilation warnings with gcc-4.0.2 on SUSE 10.0.
+       - Document extended attribute ($EA) NEED_EA flag.  (Based on libntfs
+         patch by Yura Pakhuchiy.)
+
 2.1.24 - Lots of bug fixes and support more clean journal states.
 
        - Support journals ($LogFile) which have been modified by chkdsk.  This
index 894b2b876d353b5c61a81e30d39ef608993f9000..d0d45d1c853a95f495755d9339b92f23b489ee13 100644 (file)
@@ -6,7 +6,7 @@ ntfs-objs := aops.o attrib.o collate.o compress.o debug.o dir.o file.o \
             index.o inode.o mft.o mst.o namei.o runlist.o super.o sysctl.o \
             unistr.o upcase.o
 
-EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.24\"
+EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.25\"
 
 ifeq ($(CONFIG_NTFS_DEBUG),y)
 EXTRA_CFLAGS += -DDEBUG
index 5e80c07c6a4d2b0a9c141602efeac6f9b5b59614..1c0a4315876aec7ecd7f2e8a42c603562e916f01 100644 (file)
@@ -1391,8 +1391,7 @@ retry_writepage:
                if (NInoEncrypted(ni)) {
                        unlock_page(page);
                        BUG_ON(ni->type != AT_DATA);
-                       ntfs_debug("Denying write access to encrypted "
-                                       "file.");
+                       ntfs_debug("Denying write access to encrypted file.");
                        return -EACCES;
                }
                /* Compressed data streams are handled in compress.c. */
@@ -1508,8 +1507,8 @@ retry_writepage:
        /* Zero out of bounds area in the page cache page. */
        memset(kaddr + attr_len, 0, PAGE_CACHE_SIZE - attr_len);
        kunmap_atomic(kaddr, KM_USER0);
-       flush_dcache_mft_record_page(ctx->ntfs_ino);
        flush_dcache_page(page);
+       flush_dcache_mft_record_page(ctx->ntfs_ino);
        /* We are done with the page. */
        end_page_writeback(page);
        /* Finally, mark the mft record dirty, so it gets written back. */
@@ -1542,830 +1541,6 @@ err_out:
        return err;
 }
 
-/**
- * ntfs_prepare_nonresident_write -
- *
- */
-static int ntfs_prepare_nonresident_write(struct page *page,
-               unsigned from, unsigned to)
-{
-       VCN vcn;
-       LCN lcn;
-       s64 initialized_size;
-       loff_t i_size;
-       sector_t block, ablock, iblock;
-       struct inode *vi;
-       ntfs_inode *ni;
-       ntfs_volume *vol;
-       runlist_element *rl;
-       struct buffer_head *bh, *head, *wait[2], **wait_bh = wait;
-       unsigned long flags;
-       unsigned int vcn_ofs, block_start, block_end, blocksize;
-       int err;
-       BOOL is_retry;
-       unsigned char blocksize_bits;
-
-       vi = page->mapping->host;
-       ni = NTFS_I(vi);
-       vol = ni->vol;
-
-       ntfs_debug("Entering for inode 0x%lx, attribute type 0x%x, page index "
-                       "0x%lx, from = %u, to = %u.", ni->mft_no, ni->type,
-                       page->index, from, to);
-
-       BUG_ON(!NInoNonResident(ni));
-
-       blocksize_bits = vi->i_blkbits;
-       blocksize = 1 << blocksize_bits;
-
-       /*
-        * create_empty_buffers() will create uptodate/dirty buffers if the
-        * page is uptodate/dirty.
-        */
-       if (!page_has_buffers(page))
-               create_empty_buffers(page, blocksize, 0);
-       bh = head = page_buffers(page);
-       if (unlikely(!bh))
-               return -ENOMEM;
-
-       /* The first block in the page. */
-       block = (s64)page->index << (PAGE_CACHE_SHIFT - blocksize_bits);
-
-       read_lock_irqsave(&ni->size_lock, flags);
-       /*
-        * The first out of bounds block for the allocated size.  No need to
-        * round up as allocated_size is in multiples of cluster size and the
-        * minimum cluster size is 512 bytes, which is equal to the smallest
-        * blocksize.
-        */
-       ablock = ni->allocated_size >> blocksize_bits;
-       i_size = i_size_read(vi);
-       initialized_size = ni->initialized_size;
-       read_unlock_irqrestore(&ni->size_lock, flags);
-
-       /* The last (fully or partially) initialized block. */
-       iblock = initialized_size >> blocksize_bits;
-
-       /* Loop through all the buffers in the page. */
-       block_start = 0;
-       rl = NULL;
-       err = 0;
-       do {
-               block_end = block_start + blocksize;
-               /*
-                * If buffer @bh is outside the write, just mark it uptodate
-                * if the page is uptodate and continue with the next buffer.
-                */
-               if (block_end <= from || block_start >= to) {
-                       if (PageUptodate(page)) {
-                               if (!buffer_uptodate(bh))
-                                       set_buffer_uptodate(bh);
-                       }
-                       continue;
-               }
-               /*
-                * @bh is at least partially being written to.
-                * Make sure it is not marked as new.
-                */
-               //if (buffer_new(bh))
-               //      clear_buffer_new(bh);
-
-               if (block >= ablock) {
-                       // TODO: block is above allocated_size, need to
-                       // allocate it. Best done in one go to accommodate not
-                       // only block but all above blocks up to and including:
-                       // ((page->index << PAGE_CACHE_SHIFT) + to + blocksize
-                       // - 1) >> blobksize_bits. Obviously will need to round
-                       // up to next cluster boundary, too. This should be
-                       // done with a helper function, so it can be reused.
-                       ntfs_error(vol->sb, "Writing beyond allocated size "
-                                       "is not supported yet. Sorry.");
-                       err = -EOPNOTSUPP;
-                       goto err_out;
-                       // Need to update ablock.
-                       // Need to set_buffer_new() on all block bhs that are
-                       // newly allocated.
-               }
-               /*
-                * Now we have enough allocated size to fulfill the whole
-                * request, i.e. block < ablock is true.
-                */
-               if (unlikely((block >= iblock) &&
-                               (initialized_size < i_size))) {
-                       /*
-                        * If this page is fully outside initialized size, zero
-                        * out all pages between the current initialized size
-                        * and the current page. Just use ntfs_readpage() to do
-                        * the zeroing transparently.
-                        */
-                       if (block > iblock) {
-                               // TODO:
-                               // For each page do:
-                               // - read_cache_page()
-                               // Again for each page do:
-                               // - wait_on_page_locked()
-                               // - Check (PageUptodate(page) &&
-                               //                      !PageError(page))
-                               // Update initialized size in the attribute and
-                               // in the inode.
-                               // Again, for each page do:
-                               //      __set_page_dirty_buffers();
-                               // page_cache_release()
-                               // We don't need to wait on the writes.
-                               // Update iblock.
-                       }
-                       /*
-                        * The current page straddles initialized size. Zero
-                        * all non-uptodate buffers and set them uptodate (and
-                        * dirty?). Note, there aren't any non-uptodate buffers
-                        * if the page is uptodate.
-                        * FIXME: For an uptodate page, the buffers may need to
-                        * be written out because they were not initialized on
-                        * disk before.
-                        */
-                       if (!PageUptodate(page)) {
-                               // TODO:
-                               // Zero any non-uptodate buffers up to i_size.
-                               // Set them uptodate and dirty.
-                       }
-                       // TODO:
-                       // Update initialized size in the attribute and in the
-                       // inode (up to i_size).
-                       // Update iblock.
-                       // FIXME: This is inefficient. Try to batch the two
-                       // size changes to happen in one go.
-                       ntfs_error(vol->sb, "Writing beyond initialized size "
-                                       "is not supported yet. Sorry.");
-                       err = -EOPNOTSUPP;
-                       goto err_out;
-                       // Do NOT set_buffer_new() BUT DO clear buffer range
-                       // outside write request range.
-                       // set_buffer_uptodate() on complete buffers as well as
-                       // set_buffer_dirty().
-               }
-
-               /* Need to map unmapped buffers. */
-               if (!buffer_mapped(bh)) {
-                       /* Unmapped buffer. Need to map it. */
-                       bh->b_bdev = vol->sb->s_bdev;
-
-                       /* Convert block into corresponding vcn and offset. */
-                       vcn = (VCN)block << blocksize_bits >>
-                                       vol->cluster_size_bits;
-                       vcn_ofs = ((VCN)block << blocksize_bits) &
-                                       vol->cluster_size_mask;
-
-                       is_retry = FALSE;
-                       if (!rl) {
-lock_retry_remap:
-                               down_read(&ni->runlist.lock);
-                               rl = ni->runlist.rl;
-                       }
-                       if (likely(rl != NULL)) {
-                               /* Seek to element containing target vcn. */
-                               while (rl->length && rl[1].vcn <= vcn)
-                                       rl++;
-                               lcn = ntfs_rl_vcn_to_lcn(rl, vcn);
-                       } else
-                               lcn = LCN_RL_NOT_MAPPED;
-                       if (unlikely(lcn < 0)) {
-                               /*
-                                * We extended the attribute allocation above.
-                                * If we hit an ENOENT here it means that the
-                                * allocation was insufficient which is a bug.
-                                */
-                               BUG_ON(lcn == LCN_ENOENT);
-
-                               /* It is a hole, need to instantiate it. */
-                               if (lcn == LCN_HOLE) {
-                                       // TODO: Instantiate the hole.
-                                       // clear_buffer_new(bh);
-                                       // unmap_underlying_metadata(bh->b_bdev,
-                                       //              bh->b_blocknr);
-                                       // For non-uptodate buffers, need to
-                                       // zero out the region outside the
-                                       // request in this bh or all bhs,
-                                       // depending on what we implemented
-                                       // above.
-                                       // Need to flush_dcache_page().
-                                       // Or could use set_buffer_new()
-                                       // instead?
-                                       ntfs_error(vol->sb, "Writing into "
-                                                       "sparse regions is "
-                                                       "not supported yet. "
-                                                       "Sorry.");
-                                       err = -EOPNOTSUPP;
-                                       if (!rl)
-                                               up_read(&ni->runlist.lock);
-                                       goto err_out;
-                               } else if (!is_retry &&
-                                               lcn == LCN_RL_NOT_MAPPED) {
-                                       is_retry = TRUE;
-                                       /*
-                                        * Attempt to map runlist, dropping
-                                        * lock for the duration.
-                                        */
-                                       up_read(&ni->runlist.lock);
-                                       err = ntfs_map_runlist(ni, vcn);
-                                       if (likely(!err))
-                                               goto lock_retry_remap;
-                                       rl = NULL;
-                               } else if (!rl)
-                                       up_read(&ni->runlist.lock);
-                               /*
-                                * Failed to map the buffer, even after
-                                * retrying.
-                                */
-                               if (!err)
-                                       err = -EIO;
-                               bh->b_blocknr = -1;
-                               ntfs_error(vol->sb, "Failed to write to inode "
-                                               "0x%lx, attribute type 0x%x, "
-                                               "vcn 0x%llx, offset 0x%x "
-                                               "because its location on disk "
-                                               "could not be determined%s "
-                                               "(error code %i).",
-                                               ni->mft_no, ni->type,
-                                               (unsigned long long)vcn,
-                                               vcn_ofs, is_retry ? " even "
-                                               "after retrying" : "", err);
-                               goto err_out;
-                       }
-                       /* We now have a successful remap, i.e. lcn >= 0. */
-
-                       /* Setup buffer head to correct block. */
-                       bh->b_blocknr = ((lcn << vol->cluster_size_bits)
-                                       + vcn_ofs) >> blocksize_bits;
-                       set_buffer_mapped(bh);
-
-                       // FIXME: Something analogous to this is needed for
-                       // each newly allocated block, i.e. BH_New.
-                       // FIXME: Might need to take this out of the
-                       // if (!buffer_mapped(bh)) {}, depending on how we
-                       // implement things during the allocated_size and
-                       // initialized_size extension code above.
-                       if (buffer_new(bh)) {
-                               clear_buffer_new(bh);
-                               unmap_underlying_metadata(bh->b_bdev,
-                                               bh->b_blocknr);
-                               if (PageUptodate(page)) {
-                                       set_buffer_uptodate(bh);
-                                       continue;
-                               }
-                               /*
-                                * Page is _not_ uptodate, zero surrounding
-                                * region. NOTE: This is how we decide if to
-                                * zero or not!
-                                */
-                               if (block_end > to || block_start < from) {
-                                       void *kaddr;
-
-                                       kaddr = kmap_atomic(page, KM_USER0);
-                                       if (block_end > to)
-                                               memset(kaddr + to, 0,
-                                                               block_end - to);
-                                       if (block_start < from)
-                                               memset(kaddr + block_start, 0,
-                                                               from -
-                                                               block_start);
-                                       flush_dcache_page(page);
-                                       kunmap_atomic(kaddr, KM_USER0);
-                               }
-                               continue;
-                       }
-               }
-               /* @bh is mapped, set it uptodate if the page is uptodate. */
-               if (PageUptodate(page)) {
-                       if (!buffer_uptodate(bh))
-                               set_buffer_uptodate(bh);
-                       continue;
-               }
-               /*
-                * The page is not uptodate. The buffer is mapped. If it is not
-                * uptodate, and it is only partially being written to, we need
-                * to read the buffer in before the write, i.e. right now.
-                */
-               if (!buffer_uptodate(bh) &&
-                               (block_start < from || block_end > to)) {
-                       ll_rw_block(READ, 1, &bh);
-                       *wait_bh++ = bh;
-               }
-       } while (block++, block_start = block_end,
-                       (bh = bh->b_this_page) != head);
-
-       /* Release the lock if we took it. */
-       if (rl) {
-               up_read(&ni->runlist.lock);
-               rl = NULL;
-       }
-
-       /* If we issued read requests, let them complete. */
-       while (wait_bh > wait) {
-               wait_on_buffer(*--wait_bh);
-               if (!buffer_uptodate(*wait_bh))
-                       return -EIO;
-       }
-
-       ntfs_debug("Done.");
-       return 0;
-err_out:
-       /*
-        * Zero out any newly allocated blocks to avoid exposing stale data.
-        * If BH_New is set, we know that the block was newly allocated in the
-        * above loop.
-        * FIXME: What about initialized_size increments? Have we done all the
-        * required zeroing above? If not this error handling is broken, and
-        * in particular the if (block_end <= from) check is completely bogus.
-        */
-       bh = head;
-       block_start = 0;
-       is_retry = FALSE;
-       do {
-               block_end = block_start + blocksize;
-               if (block_end <= from)
-                       continue;
-               if (block_start >= to)
-                       break;
-               if (buffer_new(bh)) {
-                       void *kaddr;
-
-                       clear_buffer_new(bh);
-                       kaddr = kmap_atomic(page, KM_USER0);
-                       memset(kaddr + block_start, 0, bh->b_size);
-                       kunmap_atomic(kaddr, KM_USER0);
-                       set_buffer_uptodate(bh);
-                       mark_buffer_dirty(bh);
-                       is_retry = TRUE;
-               }
-       } while (block_start = block_end, (bh = bh->b_this_page) != head);
-       if (is_retry)
-               flush_dcache_page(page);
-       if (rl)
-               up_read(&ni->runlist.lock);
-       return err;
-}
-
-/**
- * ntfs_prepare_write - prepare a page for receiving data
- *
- * This is called from generic_file_write() with i_sem held on the inode
- * (@page->mapping->host).  The @page is locked but not kmap()ped.  The source
- * data has not yet been copied into the @page.
- *
- * Need to extend the attribute/fill in holes if necessary, create blocks and
- * make partially overwritten blocks uptodate,
- *
- * i_size is not to be modified yet.
- *
- * Return 0 on success or -errno on error.
- *
- * Should be using block_prepare_write() [support for sparse files] or
- * cont_prepare_write() [no support for sparse files].  Cannot do that due to
- * ntfs specifics but can look at them for implementation guidance.
- *
- * Note: In the range, @from is inclusive and @to is exclusive, i.e. @from is
- * the first byte in the page that will be written to and @to is the first byte
- * after the last byte that will be written to.
- */
-static int ntfs_prepare_write(struct file *file, struct page *page,
-               unsigned from, unsigned to)
-{
-       s64 new_size;
-       loff_t i_size;
-       struct inode *vi = page->mapping->host;
-       ntfs_inode *base_ni = NULL, *ni = NTFS_I(vi);
-       ntfs_volume *vol = ni->vol;
-       ntfs_attr_search_ctx *ctx = NULL;
-       MFT_RECORD *m = NULL;
-       ATTR_RECORD *a;
-       u8 *kaddr;
-       u32 attr_len;
-       int err;
-
-       ntfs_debug("Entering for inode 0x%lx, attribute type 0x%x, page index "
-                       "0x%lx, from = %u, to = %u.", vi->i_ino, ni->type,
-                       page->index, from, to);
-       BUG_ON(!PageLocked(page));
-       BUG_ON(from > PAGE_CACHE_SIZE);
-       BUG_ON(to > PAGE_CACHE_SIZE);
-       BUG_ON(from > to);
-       BUG_ON(NInoMstProtected(ni));
-       /*
-        * If a previous ntfs_truncate() failed, repeat it and abort if it
-        * fails again.
-        */
-       if (unlikely(NInoTruncateFailed(ni))) {
-               down_write(&vi->i_alloc_sem);
-               err = ntfs_truncate(vi);
-               up_write(&vi->i_alloc_sem);
-               if (err || NInoTruncateFailed(ni)) {
-                       if (!err)
-                               err = -EIO;
-                       goto err_out;
-               }
-       }
-       /* If the attribute is not resident, deal with it elsewhere. */
-       if (NInoNonResident(ni)) {
-               /*
-                * Only unnamed $DATA attributes can be compressed, encrypted,
-                * and/or sparse.
-                */
-               if (ni->type == AT_DATA && !ni->name_len) {
-                       /* If file is encrypted, deny access, just like NT4. */
-                       if (NInoEncrypted(ni)) {
-                               ntfs_debug("Denying write access to encrypted "
-                                               "file.");
-                               return -EACCES;
-                       }
-                       /* Compressed data streams are handled in compress.c. */
-                       if (NInoCompressed(ni)) {
-                               // TODO: Implement and replace this check with
-                               // return ntfs_write_compressed_block(page);
-                               ntfs_error(vi->i_sb, "Writing to compressed "
-                                               "files is not supported yet. "
-                                               "Sorry.");
-                               return -EOPNOTSUPP;
-                       }
-                       // TODO: Implement and remove this check.
-                       if (NInoSparse(ni)) {
-                               ntfs_error(vi->i_sb, "Writing to sparse files "
-                                               "is not supported yet. Sorry.");
-                               return -EOPNOTSUPP;
-                       }
-               }
-               /* Normal data stream. */
-               return ntfs_prepare_nonresident_write(page, from, to);
-       }
-       /*
-        * Attribute is resident, implying it is not compressed, encrypted, or
-        * sparse.
-        */
-       BUG_ON(page_has_buffers(page));
-       new_size = ((s64)page->index << PAGE_CACHE_SHIFT) + to;
-       /* If we do not need to resize the attribute allocation we are done. */
-       if (new_size <= i_size_read(vi))
-               goto done;
-       /* Map, pin, and lock the (base) mft record. */
-       if (!NInoAttr(ni))
-               base_ni = ni;
-       else
-               base_ni = ni->ext.base_ntfs_ino;
-       m = map_mft_record(base_ni);
-       if (IS_ERR(m)) {
-               err = PTR_ERR(m);
-               m = NULL;
-               ctx = NULL;
-               goto err_out;
-       }
-       ctx = ntfs_attr_get_search_ctx(base_ni, m);
-       if (unlikely(!ctx)) {
-               err = -ENOMEM;
-               goto err_out;
-       }
-       err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
-                       CASE_SENSITIVE, 0, NULL, 0, ctx);
-       if (unlikely(err)) {
-               if (err == -ENOENT)
-                       err = -EIO;
-               goto err_out;
-       }
-       m = ctx->mrec;
-       a = ctx->attr;
-       /* The total length of the attribute value. */
-       attr_len = le32_to_cpu(a->data.resident.value_length);
-       /* Fix an eventual previous failure of ntfs_commit_write(). */
-       i_size = i_size_read(vi);
-       if (unlikely(attr_len > i_size)) {
-               attr_len = i_size;
-               a->data.resident.value_length = cpu_to_le32(attr_len);
-       }
-       /* If we do not need to resize the attribute allocation we are done. */
-       if (new_size <= attr_len)
-               goto done_unm;
-       /* Check if new size is allowed in $AttrDef. */
-       err = ntfs_attr_size_bounds_check(vol, ni->type, new_size);
-       if (unlikely(err)) {
-               if (err == -ERANGE) {
-                       ntfs_error(vol->sb, "Write would cause the inode "
-                                       "0x%lx to exceed the maximum size for "
-                                       "its attribute type (0x%x).  Aborting "
-                                       "write.", vi->i_ino,
-                                       le32_to_cpu(ni->type));
-               } else {
-                       ntfs_error(vol->sb, "Inode 0x%lx has unknown "
-                                       "attribute type 0x%x.  Aborting "
-                                       "write.", vi->i_ino,
-                                       le32_to_cpu(ni->type));
-                       err = -EIO;
-               }
-               goto err_out2;
-       }
-       /*
-        * Extend the attribute record to be able to store the new attribute
-        * size.
-        */
-       if (new_size >= vol->mft_record_size || ntfs_attr_record_resize(m, a,
-                       le16_to_cpu(a->data.resident.value_offset) +
-                       new_size)) {
-               /* Not enough space in the mft record. */
-               ntfs_error(vol->sb, "Not enough space in the mft record for "
-                               "the resized attribute value.  This is not "
-                               "supported yet.  Aborting write.");
-               err = -EOPNOTSUPP;
-               goto err_out2;
-       }
-       /*
-        * We have enough space in the mft record to fit the write.  This
-        * implies the attribute is smaller than the mft record and hence the
-        * attribute must be in a single page and hence page->index must be 0.
-        */
-       BUG_ON(page->index);
-       /*
-        * If the beginning of the write is past the old size, enlarge the
-        * attribute value up to the beginning of the write and fill it with
-        * zeroes.
-        */
-       if (from > attr_len) {
-               memset((u8*)a + le16_to_cpu(a->data.resident.value_offset) +
-                               attr_len, 0, from - attr_len);
-               a->data.resident.value_length = cpu_to_le32(from);
-               /* Zero the corresponding area in the page as well. */
-               if (PageUptodate(page)) {
-                       kaddr = kmap_atomic(page, KM_USER0);
-                       memset(kaddr + attr_len, 0, from - attr_len);
-                       kunmap_atomic(kaddr, KM_USER0);
-                       flush_dcache_page(page);
-               }
-       }
-       flush_dcache_mft_record_page(ctx->ntfs_ino);
-       mark_mft_record_dirty(ctx->ntfs_ino);
-done_unm:
-       ntfs_attr_put_search_ctx(ctx);
-       unmap_mft_record(base_ni);
-       /*
-        * Because resident attributes are handled by memcpy() to/from the
-        * corresponding MFT record, and because this form of i/o is byte
-        * aligned rather than block aligned, there is no need to bring the
-        * page uptodate here as in the non-resident case where we need to
-        * bring the buffers straddled by the write uptodate before
-        * generic_file_write() does the copying from userspace.
-        *
-        * We thus defer the uptodate bringing of the page region outside the
-        * region written to to ntfs_commit_write(), which makes the code
-        * simpler and saves one atomic kmap which is good.
-        */
-done:
-       ntfs_debug("Done.");
-       return 0;
-err_out:
-       if (err == -ENOMEM)
-               ntfs_warning(vi->i_sb, "Error allocating memory required to "
-                               "prepare the write.");
-       else {
-               ntfs_error(vi->i_sb, "Resident attribute prepare write failed "
-                               "with error %i.", err);
-               NVolSetErrors(vol);
-               make_bad_inode(vi);
-       }
-err_out2:
-       if (ctx)
-               ntfs_attr_put_search_ctx(ctx);
-       if (m)
-               unmap_mft_record(base_ni);
-       return err;
-}
-
-/**
- * ntfs_commit_nonresident_write -
- *
- */
-static int ntfs_commit_nonresident_write(struct page *page,
-               unsigned from, unsigned to)
-{
-       s64 pos = ((s64)page->index << PAGE_CACHE_SHIFT) + to;
-       struct inode *vi = page->mapping->host;
-       struct buffer_head *bh, *head;
-       unsigned int block_start, block_end, blocksize;
-       BOOL partial;
-
-       ntfs_debug("Entering for inode 0x%lx, attribute type 0x%x, page index "
-                       "0x%lx, from = %u, to = %u.", vi->i_ino,
-                       NTFS_I(vi)->type, page->index, from, to);
-       blocksize = 1 << vi->i_blkbits;
-
-       // FIXME: We need a whole slew of special cases in here for compressed
-       // files for example...
-       // For now, we know ntfs_prepare_write() would have failed so we can't
-       // get here in any of the cases which we have to special case, so we
-       // are just a ripped off, unrolled generic_commit_write().
-
-       bh = head = page_buffers(page);
-       block_start = 0;
-       partial = FALSE;
-       do {
-               block_end = block_start + blocksize;
-               if (block_end <= from || block_start >= to) {
-                       if (!buffer_uptodate(bh))
-                               partial = TRUE;
-               } else {
-                       set_buffer_uptodate(bh);
-                       mark_buffer_dirty(bh);
-               }
-       } while (block_start = block_end, (bh = bh->b_this_page) != head);
-       /*
-        * If this is a partial write which happened to make all buffers
-        * uptodate then we can optimize away a bogus ->readpage() for the next
-        * read().  Here we 'discover' whether the page went uptodate as a
-        * result of this (potentially partial) write.
-        */
-       if (!partial)
-               SetPageUptodate(page);
-       /*
-        * Not convinced about this at all.  See disparity comment above.  For
-        * now we know ntfs_prepare_write() would have failed in the write
-        * exceeds i_size case, so this will never trigger which is fine.
-        */
-       if (pos > i_size_read(vi)) {
-               ntfs_error(vi->i_sb, "Writing beyond the existing file size is "
-                               "not supported yet.  Sorry.");
-               return -EOPNOTSUPP;
-               // vi->i_size = pos;
-               // mark_inode_dirty(vi);
-       }
-       ntfs_debug("Done.");
-       return 0;
-}
-
-/**
- * ntfs_commit_write - commit the received data
- *
- * This is called from generic_file_write() with i_sem held on the inode
- * (@page->mapping->host).  The @page is locked but not kmap()ped.  The source
- * data has already been copied into the @page.  ntfs_prepare_write() has been
- * called before the data copied and it returned success so we can take the
- * results of various BUG checks and some error handling for granted.
- *
- * Need to mark modified blocks dirty so they get written out later when
- * ntfs_writepage() is invoked by the VM.
- *
- * Return 0 on success or -errno on error.
- *
- * Should be using generic_commit_write().  This marks buffers uptodate and
- * dirty, sets the page uptodate if all buffers in the page are uptodate, and
- * updates i_size if the end of io is beyond i_size.  In that case, it also
- * marks the inode dirty.
- *
- * Cannot use generic_commit_write() due to ntfs specialities but can look at
- * it for implementation guidance.
- *
- * If things have gone as outlined in ntfs_prepare_write(), then we do not
- * need to do any page content modifications here at all, except in the write
- * to resident attribute case, where we need to do the uptodate bringing here
- * which we combine with the copying into the mft record which means we save
- * one atomic kmap.
- */
-static int ntfs_commit_write(struct file *file, struct page *page,
-               unsigned from, unsigned to)
-{
-       struct inode *vi = page->mapping->host;
-       ntfs_inode *base_ni, *ni = NTFS_I(vi);
-       char *kaddr, *kattr;
-       ntfs_attr_search_ctx *ctx;
-       MFT_RECORD *m;
-       ATTR_RECORD *a;
-       u32 attr_len;
-       int err;
-
-       ntfs_debug("Entering for inode 0x%lx, attribute type 0x%x, page index "
-                       "0x%lx, from = %u, to = %u.", vi->i_ino, ni->type,
-                       page->index, from, to);
-       /* If the attribute is not resident, deal with it elsewhere. */
-       if (NInoNonResident(ni)) {
-               /* Only unnamed $DATA attributes can be compressed/encrypted. */
-               if (ni->type == AT_DATA && !ni->name_len) {
-                       /* Encrypted files need separate handling. */
-                       if (NInoEncrypted(ni)) {
-                               // We never get here at present!
-                               BUG();
-                       }
-                       /* Compressed data streams are handled in compress.c. */
-                       if (NInoCompressed(ni)) {
-                               // TODO: Implement this!
-                               // return ntfs_write_compressed_block(page);
-                               // We never get here at present!
-                               BUG();
-                       }
-               }
-               /* Normal data stream. */
-               return ntfs_commit_nonresident_write(page, from, to);
-       }
-       /*
-        * Attribute is resident, implying it is not compressed, encrypted, or
-        * sparse.
-        */
-       if (!NInoAttr(ni))
-               base_ni = ni;
-       else
-               base_ni = ni->ext.base_ntfs_ino;
-       /* Map, pin, and lock the mft record. */
-       m = map_mft_record(base_ni);
-       if (IS_ERR(m)) {
-               err = PTR_ERR(m);
-               m = NULL;
-               ctx = NULL;
-               goto err_out;
-       }
-       ctx = ntfs_attr_get_search_ctx(base_ni, m);
-       if (unlikely(!ctx)) {
-               err = -ENOMEM;
-               goto err_out;
-       }
-       err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
-                       CASE_SENSITIVE, 0, NULL, 0, ctx);
-       if (unlikely(err)) {
-               if (err == -ENOENT)
-                       err = -EIO;
-               goto err_out;
-       }
-       a = ctx->attr;
-       /* The total length of the attribute value. */
-       attr_len = le32_to_cpu(a->data.resident.value_length);
-       BUG_ON(from > attr_len);
-       kattr = (u8*)a + le16_to_cpu(a->data.resident.value_offset);
-       kaddr = kmap_atomic(page, KM_USER0);
-       /* Copy the received data from the page to the mft record. */
-       memcpy(kattr + from, kaddr + from, to - from);
-       /* Update the attribute length if necessary. */
-       if (to > attr_len) {
-               attr_len = to;
-               a->data.resident.value_length = cpu_to_le32(attr_len);
-       }
-       /*
-        * If the page is not uptodate, bring the out of bounds area(s)
-        * uptodate by copying data from the mft record to the page.
-        */
-       if (!PageUptodate(page)) {
-               if (from > 0)
-                       memcpy(kaddr, kattr, from);
-               if (to < attr_len)
-                       memcpy(kaddr + to, kattr + to, attr_len - to);
-               /* Zero the region outside the end of the attribute value. */
-               if (attr_len < PAGE_CACHE_SIZE)
-                       memset(kaddr + attr_len, 0, PAGE_CACHE_SIZE - attr_len);
-               /*
-                * The probability of not having done any of the above is
-                * extremely small, so we just flush unconditionally.
-                */
-               flush_dcache_page(page);
-               SetPageUptodate(page);
-       }
-       kunmap_atomic(kaddr, KM_USER0);
-       /* Update i_size if necessary. */
-       if (i_size_read(vi) < attr_len) {
-               unsigned long flags;
-
-               write_lock_irqsave(&ni->size_lock, flags);
-               ni->allocated_size = ni->initialized_size = attr_len;
-               i_size_write(vi, attr_len);
-               write_unlock_irqrestore(&ni->size_lock, flags);
-       }
-       /* Mark the mft record dirty, so it gets written back. */
-       flush_dcache_mft_record_page(ctx->ntfs_ino);
-       mark_mft_record_dirty(ctx->ntfs_ino);
-       ntfs_attr_put_search_ctx(ctx);
-       unmap_mft_record(base_ni);
-       ntfs_debug("Done.");
-       return 0;
-err_out:
-       if (err == -ENOMEM) {
-               ntfs_warning(vi->i_sb, "Error allocating memory required to "
-                               "commit the write.");
-               if (PageUptodate(page)) {
-                       ntfs_warning(vi->i_sb, "Page is uptodate, setting "
-                                       "dirty so the write will be retried "
-                                       "later on by the VM.");
-                       /*
-                        * Put the page on mapping->dirty_pages, but leave its
-                        * buffers' dirty state as-is.
-                        */
-                       __set_page_dirty_nobuffers(page);
-                       err = 0;
-               } else
-                       ntfs_error(vi->i_sb, "Page is not uptodate.  Written "
-                                       "data has been lost.");
-       } else {
-               ntfs_error(vi->i_sb, "Resident attribute commit write failed "
-                               "with error %i.", err);
-               NVolSetErrors(ni->vol);
-               make_bad_inode(vi);
-       }
-       if (ctx)
-               ntfs_attr_put_search_ctx(ctx);
-       if (m)
-               unmap_mft_record(base_ni);
-       return err;
-}
-
 #endif /* NTFS_RW */
 
 /**
@@ -2377,9 +1552,6 @@ struct address_space_operations ntfs_aops = {
                                                   disk request queue. */
 #ifdef NTFS_RW
        .writepage      = ntfs_writepage,       /* Write dirty page to disk. */
-       .prepare_write  = ntfs_prepare_write,   /* Prepare page and buffers
-                                                  ready to receive data. */
-       .commit_write   = ntfs_commit_write,    /* Commit received data. */
 #endif /* NTFS_RW */
 };
 
index 3f9a4ff42ee51b07b75e57691997087df11a181d..eda056bac2567a51c99ef521347e066ac04f86a5 100644 (file)
@@ -21,7 +21,9 @@
  */
 
 #include <linux/buffer_head.h>
+#include <linux/sched.h>
 #include <linux/swap.h>
+#include <linux/writeback.h>
 
 #include "attrib.h"
 #include "debug.h"
  * ntfs_map_runlist_nolock - map (a part of) a runlist of an ntfs inode
  * @ni:                ntfs inode for which to map (part of) a runlist
  * @vcn:       map runlist part containing this vcn
+ * @ctx:       active attribute search context if present or NULL if not
  *
  * Map the part of a runlist containing the @vcn of the ntfs inode @ni.
  *
+ * If @ctx is specified, it is an active search context of @ni and its base mft
+ * record.  This is needed when ntfs_map_runlist_nolock() encounters unmapped
+ * runlist fragments and allows their mapping.  If you do not have the mft
+ * record mapped, you can specify @ctx as NULL and ntfs_map_runlist_nolock()
+ * will perform the necessary mapping and unmapping.
+ *
+ * Note, ntfs_map_runlist_nolock() saves the state of @ctx on entry and
+ * restores it before returning.  Thus, @ctx will be left pointing to the same
+ * attribute on return as on entry.  However, the actual pointers in @ctx may
+ * point to different memory locations on return, so you must remember to reset
+ * any cached pointers from the @ctx, i.e. after the call to
+ * ntfs_map_runlist_nolock(), you will probably want to do:
+ *     m = ctx->mrec;
+ *     a = ctx->attr;
+ * Assuming you cache ctx->attr in a variable @a of type ATTR_RECORD * and that
+ * you cache ctx->mrec in a variable @m of type MFT_RECORD *.
+ *
  * Return 0 on success and -errno on error.  There is one special error code
  * which is not an error as such.  This is -ENOENT.  It means that @vcn is out
  * of bounds of the runlist.
  * Note the runlist can be NULL after this function returns if @vcn is zero and
  * the attribute has zero allocated size, i.e. there simply is no runlist.
  *
- * Locking: - The runlist must be locked for writing.
- *         - This function modifies the runlist.
+ * WARNING: If @ctx is supplied, regardless of whether success or failure is
+ *         returned, you need to check IS_ERR(@ctx->mrec) and if TRUE the @ctx
+ *         is no longer valid, i.e. you need to either call
+ *         ntfs_attr_reinit_search_ctx() or ntfs_attr_put_search_ctx() on it.
+ *         In that case PTR_ERR(@ctx->mrec) will give you the error code for
+ *         why the mapping of the old inode failed.
+ *
+ * Locking: - The runlist described by @ni must be locked for writing on entry
+ *           and is locked on return.  Note the runlist will be modified.
+ *         - If @ctx is NULL, the base mft record of @ni must not be mapped on
+ *           entry and it will be left unmapped on return.
+ *         - If @ctx is not NULL, the base mft record must be mapped on entry
+ *           and it will be left mapped on return.
  */
-int ntfs_map_runlist_nolock(ntfs_inode *ni, VCN vcn)
+int ntfs_map_runlist_nolock(ntfs_inode *ni, VCN vcn, ntfs_attr_search_ctx *ctx)
 {
        VCN end_vcn;
+       unsigned long flags;
        ntfs_inode *base_ni;
        MFT_RECORD *m;
        ATTR_RECORD *a;
-       ntfs_attr_search_ctx *ctx;
        runlist_element *rl;
-       unsigned long flags;
+       struct page *put_this_page = NULL;
        int err = 0;
+       BOOL ctx_is_temporary, ctx_needs_reset;
+       ntfs_attr_search_ctx old_ctx = { NULL, };
 
        ntfs_debug("Mapping runlist part containing vcn 0x%llx.",
                        (unsigned long long)vcn);
@@ -66,20 +99,77 @@ int ntfs_map_runlist_nolock(ntfs_inode *ni, VCN vcn)
                base_ni = ni;
        else
                base_ni = ni->ext.base_ntfs_ino;
-       m = map_mft_record(base_ni);
-       if (IS_ERR(m))
-               return PTR_ERR(m);
-       ctx = ntfs_attr_get_search_ctx(base_ni, m);
-       if (unlikely(!ctx)) {
-               err = -ENOMEM;
-               goto err_out;
+       if (!ctx) {
+               ctx_is_temporary = ctx_needs_reset = TRUE;
+               m = map_mft_record(base_ni);
+               if (IS_ERR(m))
+                       return PTR_ERR(m);
+               ctx = ntfs_attr_get_search_ctx(base_ni, m);
+               if (unlikely(!ctx)) {
+                       err = -ENOMEM;
+                       goto err_out;
+               }
+       } else {
+               VCN allocated_size_vcn;
+
+               BUG_ON(IS_ERR(ctx->mrec));
+               a = ctx->attr;
+               BUG_ON(!a->non_resident);
+               ctx_is_temporary = FALSE;
+               end_vcn = sle64_to_cpu(a->data.non_resident.highest_vcn);
+               read_lock_irqsave(&ni->size_lock, flags);
+               allocated_size_vcn = ni->allocated_size >>
+                               ni->vol->cluster_size_bits;
+               read_unlock_irqrestore(&ni->size_lock, flags);
+               if (!a->data.non_resident.lowest_vcn && end_vcn <= 0)
+                       end_vcn = allocated_size_vcn - 1;
+               /*
+                * If we already have the attribute extent containing @vcn in
+                * @ctx, no need to look it up again.  We slightly cheat in
+                * that if vcn exceeds the allocated size, we will refuse to
+                * map the runlist below, so there is definitely no need to get
+                * the right attribute extent.
+                */
+               if (vcn >= allocated_size_vcn || (a->type == ni->type &&
+                               a->name_length == ni->name_len &&
+                               !memcmp((u8*)a + le16_to_cpu(a->name_offset),
+                               ni->name, ni->name_len) &&
+                               sle64_to_cpu(a->data.non_resident.lowest_vcn)
+                               <= vcn && end_vcn >= vcn))
+                       ctx_needs_reset = FALSE;
+               else {
+                       /* Save the old search context. */
+                       old_ctx = *ctx;
+                       /*
+                        * If the currently mapped (extent) inode is not the
+                        * base inode we will unmap it when we reinitialize the
+                        * search context which means we need to get a
+                        * reference to the page containing the mapped mft
+                        * record so we do not accidentally drop changes to the
+                        * mft record when it has not been marked dirty yet.
+                        */
+                       if (old_ctx.base_ntfs_ino && old_ctx.ntfs_ino !=
+                                       old_ctx.base_ntfs_ino) {
+                               put_this_page = old_ctx.ntfs_ino->page;
+                               page_cache_get(put_this_page);
+                       }
+                       /*
+                        * Reinitialize the search context so we can lookup the
+                        * needed attribute extent.
+                        */
+                       ntfs_attr_reinit_search_ctx(ctx);
+                       ctx_needs_reset = TRUE;
+               }
        }
-       err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
-                       CASE_SENSITIVE, vcn, NULL, 0, ctx);
-       if (unlikely(err)) {
-               if (err == -ENOENT)
-                       err = -EIO;
-               goto err_out;
+       if (ctx_needs_reset) {
+               err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
+                               CASE_SENSITIVE, vcn, NULL, 0, ctx);
+               if (unlikely(err)) {
+                       if (err == -ENOENT)
+                               err = -EIO;
+                       goto err_out;
+               }
+               BUG_ON(!ctx->attr->non_resident);
        }
        a = ctx->attr;
        /*
@@ -89,11 +179,9 @@ int ntfs_map_runlist_nolock(ntfs_inode *ni, VCN vcn)
         * ntfs_mapping_pairs_decompress() fails.
         */
        end_vcn = sle64_to_cpu(a->data.non_resident.highest_vcn) + 1;
-       if (unlikely(!a->data.non_resident.lowest_vcn && end_vcn <= 1)) {
-               read_lock_irqsave(&ni->size_lock, flags);
-               end_vcn = ni->allocated_size >> ni->vol->cluster_size_bits;
-               read_unlock_irqrestore(&ni->size_lock, flags);
-       }
+       if (!a->data.non_resident.lowest_vcn && end_vcn == 1)
+               end_vcn = sle64_to_cpu(a->data.non_resident.allocated_size) >>
+                               ni->vol->cluster_size_bits;
        if (unlikely(vcn >= end_vcn)) {
                err = -ENOENT;
                goto err_out;
@@ -104,9 +192,93 @@ int ntfs_map_runlist_nolock(ntfs_inode *ni, VCN vcn)
        else
                ni->runlist.rl = rl;
 err_out:
-       if (likely(ctx))
-               ntfs_attr_put_search_ctx(ctx);
-       unmap_mft_record(base_ni);
+       if (ctx_is_temporary) {
+               if (likely(ctx))
+                       ntfs_attr_put_search_ctx(ctx);
+               unmap_mft_record(base_ni);
+       } else if (ctx_needs_reset) {
+               /*
+                * If there is no attribute list, restoring the search context
+                * is acomplished simply by copying the saved context back over
+                * the caller supplied context.  If there is an attribute list,
+                * things are more complicated as we need to deal with mapping
+                * of mft records and resulting potential changes in pointers.
+                */
+               if (NInoAttrList(base_ni)) {
+                       /*
+                        * If the currently mapped (extent) inode is not the
+                        * one we had before, we need to unmap it and map the
+                        * old one.
+                        */
+                       if (ctx->ntfs_ino != old_ctx.ntfs_ino) {
+                               /*
+                                * If the currently mapped inode is not the
+                                * base inode, unmap it.
+                                */
+                               if (ctx->base_ntfs_ino && ctx->ntfs_ino !=
+                                               ctx->base_ntfs_ino) {
+                                       unmap_extent_mft_record(ctx->ntfs_ino);
+                                       ctx->mrec = ctx->base_mrec;
+                                       BUG_ON(!ctx->mrec);
+                               }
+                               /*
+                                * If the old mapped inode is not the base
+                                * inode, map it.
+                                */
+                               if (old_ctx.base_ntfs_ino &&
+                                               old_ctx.ntfs_ino !=
+                                               old_ctx.base_ntfs_ino) {
+retry_map:
+                                       ctx->mrec = map_mft_record(
+                                                       old_ctx.ntfs_ino);
+                                       /*
+                                        * Something bad has happened.  If out
+                                        * of memory retry till it succeeds.
+                                        * Any other errors are fatal and we
+                                        * return the error code in ctx->mrec.
+                                        * Let the caller deal with it...  We
+                                        * just need to fudge things so the
+                                        * caller can reinit and/or put the
+                                        * search context safely.
+                                        */
+                                       if (IS_ERR(ctx->mrec)) {
+                                               if (PTR_ERR(ctx->mrec) ==
+                                                               -ENOMEM) {
+                                                       schedule();
+                                                       goto retry_map;
+                                               } else
+                                                       old_ctx.ntfs_ino =
+                                                               old_ctx.
+                                                               base_ntfs_ino;
+                                       }
+                               }
+                       }
+                       /* Update the changed pointers in the saved context. */
+                       if (ctx->mrec != old_ctx.mrec) {
+                               if (!IS_ERR(ctx->mrec))
+                                       old_ctx.attr = (ATTR_RECORD*)(
+                                                       (u8*)ctx->mrec +
+                                                       ((u8*)old_ctx.attr -
+                                                       (u8*)old_ctx.mrec));
+                               old_ctx.mrec = ctx->mrec;
+                       }
+               }
+               /* Restore the search context to the saved one. */
+               *ctx = old_ctx;
+               /*
+                * We drop the reference on the page we took earlier.  In the
+                * case that IS_ERR(ctx->mrec) is true this means we might lose
+                * some changes to the mft record that had been made between
+                * the last time it was marked dirty/written out and now.  This
+                * at this stage is not a problem as the mapping error is fatal
+                * enough that the mft record cannot be written out anyway and
+                * the caller is very likely to shutdown the whole inode
+                * immediately and mark the volume dirty for chkdsk to pick up
+                * the pieces anyway.
+                */
+               if (put_this_page)
+                       page_cache_release(put_this_page);
+       }
        return err;
 }
 
@@ -122,8 +294,8 @@ err_out:
  * of bounds of the runlist.
  *
  * Locking: - The runlist must be unlocked on entry and is unlocked on return.
- *         - This function takes the runlist lock for writing and modifies the
- *           runlist.
+ *         - This function takes the runlist lock for writing and may modify
+ *           the runlist.
  */
 int ntfs_map_runlist(ntfs_inode *ni, VCN vcn)
 {
@@ -133,7 +305,7 @@ int ntfs_map_runlist(ntfs_inode *ni, VCN vcn)
        /* Make sure someone else didn't do the work while we were sleeping. */
        if (likely(ntfs_rl_vcn_to_lcn(ni->runlist.rl, vcn) <=
                        LCN_RL_NOT_MAPPED))
-               err = ntfs_map_runlist_nolock(ni, vcn);
+               err = ntfs_map_runlist_nolock(ni, vcn, NULL);
        up_write(&ni->runlist.lock);
        return err;
 }
@@ -212,7 +384,7 @@ retry_remap:
                                goto retry_remap;
                        }
                }
-               err = ntfs_map_runlist_nolock(ni, vcn);
+               err = ntfs_map_runlist_nolock(ni, vcn, NULL);
                if (!write_locked) {
                        up_write(&ni->runlist.lock);
                        down_read(&ni->runlist.lock);
@@ -236,9 +408,9 @@ retry_remap:
 
 /**
  * ntfs_attr_find_vcn_nolock - find a vcn in the runlist of an ntfs inode
- * @ni:                        ntfs inode describing the runlist to search
- * @vcn:               vcn to find
- * @write_locked:      true if the runlist is locked for writing
+ * @ni:                ntfs inode describing the runlist to search
+ * @vcn:       vcn to find
+ * @ctx:       active attribute search context if present or NULL if not
  *
  * Find the virtual cluster number @vcn in the runlist described by the ntfs
  * inode @ni and return the address of the runlist element containing the @vcn.
@@ -246,9 +418,22 @@ retry_remap:
  * If the @vcn is not mapped yet, the attempt is made to map the attribute
  * extent containing the @vcn and the vcn to lcn conversion is retried.
  *
- * If @write_locked is true the caller has locked the runlist for writing and
- * if false for reading.
- *
+ * If @ctx is specified, it is an active search context of @ni and its base mft
+ * record.  This is needed when ntfs_attr_find_vcn_nolock() encounters unmapped
+ * runlist fragments and allows their mapping.  If you do not have the mft
+ * record mapped, you can specify @ctx as NULL and ntfs_attr_find_vcn_nolock()
+ * will perform the necessary mapping and unmapping.
+ *
+ * Note, ntfs_attr_find_vcn_nolock() saves the state of @ctx on entry and
+ * restores it before returning.  Thus, @ctx will be left pointing to the same
+ * attribute on return as on entry.  However, the actual pointers in @ctx may
+ * point to different memory locations on return, so you must remember to reset
+ * any cached pointers from the @ctx, i.e. after the call to
+ * ntfs_attr_find_vcn_nolock(), you will probably want to do:
+ *     m = ctx->mrec;
+ *     a = ctx->attr;
+ * Assuming you cache ctx->attr in a variable @a of type ATTR_RECORD * and that
+ * you cache ctx->mrec in a variable @m of type MFT_RECORD *.
  * Note you need to distinguish between the lcn of the returned runlist element
  * being >= 0 and LCN_HOLE.  In the later case you have to return zeroes on
  * read and allocate clusters on write.
@@ -263,22 +448,31 @@ retry_remap:
  *     -ENOMEM - Not enough memory to map runlist.
  *     -EIO    - Critical error (runlist/file is corrupt, i/o error, etc).
  *
- * Locking: - The runlist must be locked on entry and is left locked on return.
- *         - If @write_locked is FALSE, i.e. the runlist is locked for reading,
- *           the lock may be dropped inside the function so you cannot rely on
- *           the runlist still being the same when this function returns.
+ * WARNING: If @ctx is supplied, regardless of whether success or failure is
+ *         returned, you need to check IS_ERR(@ctx->mrec) and if TRUE the @ctx
+ *         is no longer valid, i.e. you need to either call
+ *         ntfs_attr_reinit_search_ctx() or ntfs_attr_put_search_ctx() on it.
+ *         In that case PTR_ERR(@ctx->mrec) will give you the error code for
+ *         why the mapping of the old inode failed.
+ *
+ * Locking: - The runlist described by @ni must be locked for writing on entry
+ *           and is locked on return.  Note the runlist may be modified when
+ *           needed runlist fragments need to be mapped.
+ *         - If @ctx is NULL, the base mft record of @ni must not be mapped on
+ *           entry and it will be left unmapped on return.
+ *         - If @ctx is not NULL, the base mft record must be mapped on entry
+ *           and it will be left mapped on return.
  */
 runlist_element *ntfs_attr_find_vcn_nolock(ntfs_inode *ni, const VCN vcn,
-               const BOOL write_locked)
+               ntfs_attr_search_ctx *ctx)
 {
        unsigned long flags;
        runlist_element *rl;
        int err = 0;
        BOOL is_retry = FALSE;
 
-       ntfs_debug("Entering for i_ino 0x%lx, vcn 0x%llx, %s_locked.",
-                       ni->mft_no, (unsigned long long)vcn,
-                       write_locked ? "write" : "read");
+       ntfs_debug("Entering for i_ino 0x%lx, vcn 0x%llx, with%s ctx.",
+                       ni->mft_no, (unsigned long long)vcn, ctx ? "" : "out");
        BUG_ON(!ni);
        BUG_ON(!NInoNonResident(ni));
        BUG_ON(vcn < 0);
@@ -312,33 +506,22 @@ retry_remap:
        }
        if (!err && !is_retry) {
                /*
-                * The @vcn is in an unmapped region, map the runlist and
-                * retry.
+                * If the search context is invalid we cannot map the unmapped
+                * region.
                 */
-               if (!write_locked) {
-                       up_read(&ni->runlist.lock);
-                       down_write(&ni->runlist.lock);
-                       if (unlikely(ntfs_rl_vcn_to_lcn(ni->runlist.rl, vcn) !=
-                                       LCN_RL_NOT_MAPPED)) {
-                               up_write(&ni->runlist.lock);
-                               down_read(&ni->runlist.lock);
+               if (IS_ERR(ctx->mrec))
+                       err = PTR_ERR(ctx->mrec);
+               else {
+                       /*
+                        * The @vcn is in an unmapped region, map the runlist
+                        * and retry.
+                        */
+                       err = ntfs_map_runlist_nolock(ni, vcn, ctx);
+                       if (likely(!err)) {
+                               is_retry = TRUE;
                                goto retry_remap;
                        }
                }
-               err = ntfs_map_runlist_nolock(ni, vcn);
-               if (!write_locked) {
-                       up_write(&ni->runlist.lock);
-                       down_read(&ni->runlist.lock);
-               }
-               if (likely(!err)) {
-                       is_retry = TRUE;
-                       goto retry_remap;
-               }
-               /*
-                * -EINVAL coming from a failed mapping attempt is equivalent
-                * to i/o error for us as it should not happen in our code
-                * paths.
-                */
                if (err == -EINVAL)
                        err = -EIO;
        } else if (!err)
@@ -1011,6 +1194,7 @@ int ntfs_attr_lookup(const ATTR_TYPE type, const ntfschar *name,
        ntfs_inode *base_ni;
 
        ntfs_debug("Entering.");
+       BUG_ON(IS_ERR(ctx->mrec));
        if (ctx->base_ntfs_ino)
                base_ni = ctx->base_ntfs_ino;
        else
@@ -1227,7 +1411,7 @@ int ntfs_attr_can_be_non_resident(const ntfs_volume *vol, const ATTR_TYPE type)
  */
 int ntfs_attr_can_be_resident(const ntfs_volume *vol, const ATTR_TYPE type)
 {
-       if (type == AT_INDEX_ALLOCATION || type == AT_EA)
+       if (type == AT_INDEX_ALLOCATION)
                return -EPERM;
        return 0;
 }
@@ -1319,10 +1503,17 @@ int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a,
 /**
  * ntfs_attr_make_non_resident - convert a resident to a non-resident attribute
  * @ni:                ntfs inode describing the attribute to convert
+ * @data_size: size of the resident data to copy to the non-resident attribute
  *
  * Convert the resident ntfs attribute described by the ntfs inode @ni to a
  * non-resident one.
  *
+ * @data_size must be equal to the attribute value size.  This is needed since
+ * we need to know the size before we can map the mft record and our callers
+ * always know it.  The reason we cannot simply read the size from the vfs
+ * inode i_size is that this is not necessarily uptodate.  This happens when
+ * ntfs_attr_make_non_resident() is called in the ->truncate call path(s).
+ *
  * Return 0 on success and -errno on error.  The following error return codes
  * are defined:
  *     -EPERM  - The attribute is not allowed to be non-resident.
@@ -1343,7 +1534,7 @@ int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a,
  *
  * Locking: - The caller must hold i_sem on the inode.
  */
-int ntfs_attr_make_non_resident(ntfs_inode *ni)
+int ntfs_attr_make_non_resident(ntfs_inode *ni, const u32 data_size)
 {
        s64 new_size;
        struct inode *vi = VFS_I(ni);
@@ -1381,11 +1572,9 @@ int ntfs_attr_make_non_resident(ntfs_inode *ni)
         * The size needs to be aligned to a cluster boundary for allocation
         * purposes.
         */
-       new_size = (i_size_read(vi) + vol->cluster_size - 1) &
+       new_size = (data_size + vol->cluster_size - 1) &
                        ~(vol->cluster_size - 1);
        if (new_size > 0) {
-               runlist_element *rl2;
-
                /*
                 * Will need the page later and since the page lock nests
                 * outside all ntfs locks, we need to get the page now.
@@ -1396,7 +1585,7 @@ int ntfs_attr_make_non_resident(ntfs_inode *ni)
                        return -ENOMEM;
                /* Start by allocating clusters to hold the attribute value. */
                rl = ntfs_cluster_alloc(vol, 0, new_size >>
-                               vol->cluster_size_bits, -1, DATA_ZONE);
+                               vol->cluster_size_bits, -1, DATA_ZONE, TRUE);
                if (IS_ERR(rl)) {
                        err = PTR_ERR(rl);
                        ntfs_debug("Failed to allocate cluster%s, error code "
@@ -1405,12 +1594,6 @@ int ntfs_attr_make_non_resident(ntfs_inode *ni)
                                        err);
                        goto page_err_out;
                }
-               /* Change the runlist terminator to LCN_ENOENT. */
-               rl2 = rl;
-               while (rl2->length)
-                       rl2++;
-               BUG_ON(rl2->lcn != LCN_RL_NOT_MAPPED);
-               rl2->lcn = LCN_ENOENT;
        } else {
                rl = NULL;
                page = NULL;
@@ -1473,7 +1656,7 @@ int ntfs_attr_make_non_resident(ntfs_inode *ni)
         * attribute value.
         */
        attr_size = le32_to_cpu(a->data.resident.value_length);
-       BUG_ON(attr_size != i_size_read(vi));
+       BUG_ON(attr_size != data_size);
        if (page && !PageUptodate(page)) {
                kaddr = kmap_atomic(page, KM_USER0);
                memcpy(kaddr, (u8*)a +
@@ -1538,7 +1721,9 @@ int ntfs_attr_make_non_resident(ntfs_inode *ni)
                                ffs(ni->itype.compressed.block_size) - 1;
                ni->itype.compressed.block_clusters = 1U <<
                                a->data.non_resident.compression_unit;
-       }
+               vi->i_blocks = ni->itype.compressed.size >> 9;
+       } else
+               vi->i_blocks = ni->allocated_size >> 9;
        write_unlock_irqrestore(&ni->size_lock, flags);
        /*
         * This needs to be last since the address space operations ->readpage
@@ -1651,6 +1836,640 @@ page_err_out:
        return err;
 }
 
+/**
+ * ntfs_attr_extend_allocation - extend the allocated space of an attribute
+ * @ni:                        ntfs inode of the attribute whose allocation to extend
+ * @new_alloc_size:    new size in bytes to which to extend the allocation to
+ * @new_data_size:     new size in bytes to which to extend the data to
+ * @data_start:                beginning of region which is required to be non-sparse
+ *
+ * Extend the allocated space of an attribute described by the ntfs inode @ni
+ * to @new_alloc_size bytes.  If @data_start is -1, the whole extension may be
+ * implemented as a hole in the file (as long as both the volume and the ntfs
+ * inode @ni have sparse support enabled).  If @data_start is >= 0, then the
+ * region between the old allocated size and @data_start - 1 may be made sparse
+ * but the regions between @data_start and @new_alloc_size must be backed by
+ * actual clusters.
+ *
+ * If @new_data_size is -1, it is ignored.  If it is >= 0, then the data size
+ * of the attribute is extended to @new_data_size.  Note that the i_size of the
+ * vfs inode is not updated.  Only the data size in the base attribute record
+ * is updated.  The caller has to update i_size separately if this is required.
+ * WARNING: It is a BUG() for @new_data_size to be smaller than the old data
+ * size as well as for @new_data_size to be greater than @new_alloc_size.
+ *
+ * For resident attributes this involves resizing the attribute record and if
+ * necessary moving it and/or other attributes into extent mft records and/or
+ * converting the attribute to a non-resident attribute which in turn involves
+ * extending the allocation of a non-resident attribute as described below.
+ *
+ * For non-resident attributes this involves allocating clusters in the data
+ * zone on the volume (except for regions that are being made sparse) and
+ * extending the run list to describe the allocated clusters as well as
+ * updating the mapping pairs array of the attribute.  This in turn involves
+ * resizing the attribute record and if necessary moving it and/or other
+ * attributes into extent mft records and/or splitting the attribute record
+ * into multiple extent attribute records.
+ *
+ * Also, the attribute list attribute is updated if present and in some of the
+ * above cases (the ones where extent mft records/attributes come into play),
+ * an attribute list attribute is created if not already present.
+ *
+ * Return the new allocated size on success and -errno on error.  In the case
+ * that an error is encountered but a partial extension at least up to
+ * @data_start (if present) is possible, the allocation is partially extended
+ * and this is returned.  This means the caller must check the returned size to
+ * determine if the extension was partial.  If @data_start is -1 then partial
+ * allocations are not performed.
+ *
+ * WARNING: Do not call ntfs_attr_extend_allocation() for $MFT/$DATA.
+ *
+ * Locking: This function takes the runlist lock of @ni for writing as well as
+ * locking the mft record of the base ntfs inode.  These locks are maintained
+ * throughout execution of the function.  These locks are required so that the
+ * attribute can be resized safely and so that it can for example be converted
+ * from resident to non-resident safely.
+ *
+ * TODO: At present attribute list attribute handling is not implemented.
+ *
+ * TODO: At present it is not safe to call this function for anything other
+ * than the $DATA attribute(s) of an uncompressed and unencrypted file.
+ */
+s64 ntfs_attr_extend_allocation(ntfs_inode *ni, s64 new_alloc_size,
+               const s64 new_data_size, const s64 data_start)
+{
+       VCN vcn;
+       s64 ll, allocated_size, start = data_start;
+       struct inode *vi = VFS_I(ni);
+       ntfs_volume *vol = ni->vol;
+       ntfs_inode *base_ni;
+       MFT_RECORD *m;
+       ATTR_RECORD *a;
+       ntfs_attr_search_ctx *ctx;
+       runlist_element *rl, *rl2;
+       unsigned long flags;
+       int err, mp_size;
+       u32 attr_len = 0; /* Silence stupid gcc warning. */
+       BOOL mp_rebuilt;
+
+#ifdef NTFS_DEBUG
+       read_lock_irqsave(&ni->size_lock, flags);
+       allocated_size = ni->allocated_size;
+       read_unlock_irqrestore(&ni->size_lock, flags);
+       ntfs_debug("Entering for i_ino 0x%lx, attribute type 0x%x, "
+                       "old_allocated_size 0x%llx, "
+                       "new_allocated_size 0x%llx, new_data_size 0x%llx, "
+                       "data_start 0x%llx.", vi->i_ino,
+                       (unsigned)le32_to_cpu(ni->type),
+                       (unsigned long long)allocated_size,
+                       (unsigned long long)new_alloc_size,
+                       (unsigned long long)new_data_size,
+                       (unsigned long long)start);
+#endif
+retry_extend:
+       /*
+        * For non-resident attributes, @start and @new_size need to be aligned
+        * to cluster boundaries for allocation purposes.
+        */
+       if (NInoNonResident(ni)) {
+               if (start > 0)
+                       start &= ~(s64)vol->cluster_size_mask;
+               new_alloc_size = (new_alloc_size + vol->cluster_size - 1) &
+                               ~(s64)vol->cluster_size_mask;
+       }
+       BUG_ON(new_data_size >= 0 && new_data_size > new_alloc_size);
+       /* Check if new size is allowed in $AttrDef. */
+       err = ntfs_attr_size_bounds_check(vol, ni->type, new_alloc_size);
+       if (unlikely(err)) {
+               /* Only emit errors when the write will fail completely. */
+               read_lock_irqsave(&ni->size_lock, flags);
+               allocated_size = ni->allocated_size;
+               read_unlock_irqrestore(&ni->size_lock, flags);
+               if (start < 0 || start >= allocated_size) {
+                       if (err == -ERANGE) {
+                               ntfs_error(vol->sb, "Cannot extend allocation "
+                                               "of inode 0x%lx, attribute "
+                                               "type 0x%x, because the new "
+                                               "allocation would exceed the "
+                                               "maximum allowed size for "
+                                               "this attribute type.",
+                                               vi->i_ino, (unsigned)
+                                               le32_to_cpu(ni->type));
+                       } else {
+                               ntfs_error(vol->sb, "Cannot extend allocation "
+                                               "of inode 0x%lx, attribute "
+                                               "type 0x%x, because this "
+                                               "attribute type is not "
+                                               "defined on the NTFS volume.  "
+                                               "Possible corruption!  You "
+                                               "should run chkdsk!",
+                                               vi->i_ino, (unsigned)
+                                               le32_to_cpu(ni->type));
+                       }
+               }
+               /* Translate error code to be POSIX conformant for write(2). */
+               if (err == -ERANGE)
+                       err = -EFBIG;
+               else
+                       err = -EIO;
+               return err;
+       }
+       if (!NInoAttr(ni))
+               base_ni = ni;
+       else
+               base_ni = ni->ext.base_ntfs_ino;
+       /*
+        * We will be modifying both the runlist (if non-resident) and the mft
+        * record so lock them both down.
+        */
+       down_write(&ni->runlist.lock);
+       m = map_mft_record(base_ni);
+       if (IS_ERR(m)) {
+               err = PTR_ERR(m);
+               m = NULL;
+               ctx = NULL;
+               goto err_out;
+       }
+       ctx = ntfs_attr_get_search_ctx(base_ni, m);
+       if (unlikely(!ctx)) {
+               err = -ENOMEM;
+               goto err_out;
+       }
+       read_lock_irqsave(&ni->size_lock, flags);
+       allocated_size = ni->allocated_size;
+       read_unlock_irqrestore(&ni->size_lock, flags);
+       /*
+        * If non-resident, seek to the last extent.  If resident, there is
+        * only one extent, so seek to that.
+        */
+       vcn = NInoNonResident(ni) ? allocated_size >> vol->cluster_size_bits :
+                       0;
+       /*
+        * Abort if someone did the work whilst we waited for the locks.  If we
+        * just converted the attribute from resident to non-resident it is
+        * likely that exactly this has happened already.  We cannot quite
+        * abort if we need to update the data size.
+        */
+       if (unlikely(new_alloc_size <= allocated_size)) {
+               ntfs_debug("Allocated size already exceeds requested size.");
+               new_alloc_size = allocated_size;
+               if (new_data_size < 0)
+                       goto done;
+               /*
+                * We want the first attribute extent so that we can update the
+                * data size.
+                */
+               vcn = 0;
+       }
+       err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
+                       CASE_SENSITIVE, vcn, NULL, 0, ctx);
+       if (unlikely(err)) {
+               if (err == -ENOENT)
+                       err = -EIO;
+               goto err_out;
+       }
+       m = ctx->mrec;
+       a = ctx->attr;
+       /* Use goto to reduce indentation. */
+       if (a->non_resident)
+               goto do_non_resident_extend;
+       BUG_ON(NInoNonResident(ni));
+       /* The total length of the attribute value. */
+       attr_len = le32_to_cpu(a->data.resident.value_length);
+       /*
+        * Extend the attribute record to be able to store the new attribute
+        * size.  ntfs_attr_record_resize() will not do anything if the size is
+        * not changing.
+        */
+       if (new_alloc_size < vol->mft_record_size &&
+                       !ntfs_attr_record_resize(m, a,
+                       le16_to_cpu(a->data.resident.value_offset) +
+                       new_alloc_size)) {
+               /* The resize succeeded! */
+               write_lock_irqsave(&ni->size_lock, flags);
+               ni->allocated_size = le32_to_cpu(a->length) -
+                               le16_to_cpu(a->data.resident.value_offset);
+               write_unlock_irqrestore(&ni->size_lock, flags);
+               if (new_data_size >= 0) {
+                       BUG_ON(new_data_size < attr_len);
+                       a->data.resident.value_length =
+                                       cpu_to_le32((u32)new_data_size);
+               }
+               goto flush_done;
+       }
+       /*
+        * We have to drop all the locks so we can call
+        * ntfs_attr_make_non_resident().  This could be optimised by try-
+        * locking the first page cache page and only if that fails dropping
+        * the locks, locking the page, and redoing all the locking and
+        * lookups.  While this would be a huge optimisation, it is not worth
+        * it as this is definitely a slow code path.
+        */
+       ntfs_attr_put_search_ctx(ctx);
+       unmap_mft_record(base_ni);
+       up_write(&ni->runlist.lock);
+       /*
+        * Not enough space in the mft record, try to make the attribute
+        * non-resident and if successful restart the extension process.
+        */
+       err = ntfs_attr_make_non_resident(ni, attr_len);
+       if (likely(!err))
+               goto retry_extend;
+       /*
+        * Could not make non-resident.  If this is due to this not being
+        * permitted for this attribute type or there not being enough space,
+        * try to make other attributes non-resident.  Otherwise fail.
+        */
+       if (unlikely(err != -EPERM && err != -ENOSPC)) {
+               /* Only emit errors when the write will fail completely. */
+               read_lock_irqsave(&ni->size_lock, flags);
+               allocated_size = ni->allocated_size;
+               read_unlock_irqrestore(&ni->size_lock, flags);
+               if (start < 0 || start >= allocated_size)
+                       ntfs_error(vol->sb, "Cannot extend allocation of "
+                                       "inode 0x%lx, attribute type 0x%x, "
+                                       "because the conversion from resident "
+                                       "to non-resident attribute failed "
+                                       "with error code %i.", vi->i_ino,
+                                       (unsigned)le32_to_cpu(ni->type), err);
+               if (err != -ENOMEM)
+                       err = -EIO;
+               goto conv_err_out;
+       }
+       /* TODO: Not implemented from here, abort. */
+       read_lock_irqsave(&ni->size_lock, flags);
+       allocated_size = ni->allocated_size;
+       read_unlock_irqrestore(&ni->size_lock, flags);
+       if (start < 0 || start >= allocated_size) {
+               if (err == -ENOSPC)
+                       ntfs_error(vol->sb, "Not enough space in the mft "
+                                       "record/on disk for the non-resident "
+                                       "attribute value.  This case is not "
+                                       "implemented yet.");
+               else /* if (err == -EPERM) */
+                       ntfs_error(vol->sb, "This attribute type may not be "
+                                       "non-resident.  This case is not "
+                                       "implemented yet.");
+       }
+       err = -EOPNOTSUPP;
+       goto conv_err_out;
+#if 0
+       // TODO: Attempt to make other attributes non-resident.
+       if (!err)
+               goto do_resident_extend;
+       /*
+        * Both the attribute list attribute and the standard information
+        * attribute must remain in the base inode.  Thus, if this is one of
+        * these attributes, we have to try to move other attributes out into
+        * extent mft records instead.
+        */
+       if (ni->type == AT_ATTRIBUTE_LIST ||
+                       ni->type == AT_STANDARD_INFORMATION) {
+               // TODO: Attempt to move other attributes into extent mft
+               // records.
+               err = -EOPNOTSUPP;
+               if (!err)
+                       goto do_resident_extend;
+               goto err_out;
+       }
+       // TODO: Attempt to move this attribute to an extent mft record, but
+       // only if it is not already the only attribute in an mft record in
+       // which case there would be nothing to gain.
+       err = -EOPNOTSUPP;
+       if (!err)
+               goto do_resident_extend;
+       /* There is nothing we can do to make enough space. )-: */
+       goto err_out;
+#endif
+do_non_resident_extend:
+       BUG_ON(!NInoNonResident(ni));
+       if (new_alloc_size == allocated_size) {
+               BUG_ON(vcn);
+               goto alloc_done;
+       }
+       /*
+        * If the data starts after the end of the old allocation, this is a
+        * $DATA attribute and sparse attributes are enabled on the volume and
+        * for this inode, then create a sparse region between the old
+        * allocated size and the start of the data.  Otherwise simply proceed
+        * with filling the whole space between the old allocated size and the
+        * new allocated size with clusters.
+        */
+       if ((start >= 0 && start <= allocated_size) || ni->type != AT_DATA ||
+                       !NVolSparseEnabled(vol) || NInoSparseDisabled(ni))
+               goto skip_sparse;
+       // TODO: This is not implemented yet.  We just fill in with real
+       // clusters for now...
+       ntfs_debug("Inserting holes is not-implemented yet.  Falling back to "
+                       "allocating real clusters instead.");
+skip_sparse:
+       rl = ni->runlist.rl;
+       if (likely(rl)) {
+               /* Seek to the end of the runlist. */
+               while (rl->length)
+                       rl++;
+       }
+       /* If this attribute extent is not mapped, map it now. */
+       if (unlikely(!rl || rl->lcn == LCN_RL_NOT_MAPPED ||
+                       (rl->lcn == LCN_ENOENT && rl > ni->runlist.rl &&
+                       (rl-1)->lcn == LCN_RL_NOT_MAPPED))) {
+               if (!rl && !allocated_size)
+                       goto first_alloc;
+               rl = ntfs_mapping_pairs_decompress(vol, a, ni->runlist.rl);
+               if (IS_ERR(rl)) {
+                       err = PTR_ERR(rl);
+                       if (start < 0 || start >= allocated_size)
+                               ntfs_error(vol->sb, "Cannot extend allocation "
+                                               "of inode 0x%lx, attribute "
+                                               "type 0x%x, because the "
+                                               "mapping of a runlist "
+                                               "fragment failed with error "
+                                               "code %i.", vi->i_ino,
+                                               (unsigned)le32_to_cpu(ni->type),
+                                               err);
+                       if (err != -ENOMEM)
+                               err = -EIO;
+                       goto err_out;
+               }
+               ni->runlist.rl = rl;
+               /* Seek to the end of the runlist. */
+               while (rl->length)
+                       rl++;
+       }
+       /*
+        * We now know the runlist of the last extent is mapped and @rl is at
+        * the end of the runlist.  We want to begin allocating clusters
+        * starting at the last allocated cluster to reduce fragmentation.  If
+        * there are no valid LCNs in the attribute we let the cluster
+        * allocator choose the starting cluster.
+        */
+       /* If the last LCN is a hole or simillar seek back to last real LCN. */
+       while (rl->lcn < 0 && rl > ni->runlist.rl)
+               rl--;
+first_alloc:
+       // FIXME: Need to implement partial allocations so at least part of the
+       // write can be performed when start >= 0.  (Needed for POSIX write(2)
+       // conformance.)
+       rl2 = ntfs_cluster_alloc(vol, allocated_size >> vol->cluster_size_bits,
+                       (new_alloc_size - allocated_size) >>
+                       vol->cluster_size_bits, (rl && (rl->lcn >= 0)) ?
+                       rl->lcn + rl->length : -1, DATA_ZONE, TRUE);
+       if (IS_ERR(rl2)) {
+               err = PTR_ERR(rl2);
+               if (start < 0 || start >= allocated_size)
+                       ntfs_error(vol->sb, "Cannot extend allocation of "
+                                       "inode 0x%lx, attribute type 0x%x, "
+                                       "because the allocation of clusters "
+                                       "failed with error code %i.", vi->i_ino,
+                                       (unsigned)le32_to_cpu(ni->type), err);
+               if (err != -ENOMEM && err != -ENOSPC)
+                       err = -EIO;
+               goto err_out;
+       }
+       rl = ntfs_runlists_merge(ni->runlist.rl, rl2);
+       if (IS_ERR(rl)) {
+               err = PTR_ERR(rl);
+               if (start < 0 || start >= allocated_size)
+                       ntfs_error(vol->sb, "Cannot extend allocation of "
+                                       "inode 0x%lx, attribute type 0x%x, "
+                                       "because the runlist merge failed "
+                                       "with error code %i.", vi->i_ino,
+                                       (unsigned)le32_to_cpu(ni->type), err);
+               if (err != -ENOMEM)
+                       err = -EIO;
+               if (ntfs_cluster_free_from_rl(vol, rl2)) {
+                       ntfs_error(vol->sb, "Failed to release allocated "
+                                       "cluster(s) in error code path.  Run "
+                                       "chkdsk to recover the lost "
+                                       "cluster(s).");
+                       NVolSetErrors(vol);
+               }
+               ntfs_free(rl2);
+               goto err_out;
+       }
+       ni->runlist.rl = rl;
+       ntfs_debug("Allocated 0x%llx clusters.", (long long)(new_alloc_size -
+                       allocated_size) >> vol->cluster_size_bits);
+       /* Find the runlist element with which the attribute extent starts. */
+       ll = sle64_to_cpu(a->data.non_resident.lowest_vcn);
+       rl2 = ntfs_rl_find_vcn_nolock(rl, ll);
+       BUG_ON(!rl2);
+       BUG_ON(!rl2->length);
+       BUG_ON(rl2->lcn < LCN_HOLE);
+       mp_rebuilt = FALSE;
+       /* Get the size for the new mapping pairs array for this extent. */
+       mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll, -1);
+       if (unlikely(mp_size <= 0)) {
+               err = mp_size;
+               if (start < 0 || start >= allocated_size)
+                       ntfs_error(vol->sb, "Cannot extend allocation of "
+                                       "inode 0x%lx, attribute type 0x%x, "
+                                       "because determining the size for the "
+                                       "mapping pairs failed with error code "
+                                       "%i.", vi->i_ino,
+                                       (unsigned)le32_to_cpu(ni->type), err);
+               err = -EIO;
+               goto undo_alloc;
+       }
+       /* Extend the attribute record to fit the bigger mapping pairs array. */
+       attr_len = le32_to_cpu(a->length);
+       err = ntfs_attr_record_resize(m, a, mp_size +
+                       le16_to_cpu(a->data.non_resident.mapping_pairs_offset));
+       if (unlikely(err)) {
+               BUG_ON(err != -ENOSPC);
+               // TODO: Deal with this by moving this extent to a new mft
+               // record or by starting a new extent in a new mft record,
+               // possibly by extending this extent partially and filling it
+               // and creating a new extent for the remainder, or by making
+               // other attributes non-resident and/or by moving other
+               // attributes out of this mft record.
+               if (start < 0 || start >= allocated_size)
+                       ntfs_error(vol->sb, "Not enough space in the mft "
+                                       "record for the extended attribute "
+                                       "record.  This case is not "
+                                       "implemented yet.");
+               err = -EOPNOTSUPP;
+               goto undo_alloc;
+       }
+       mp_rebuilt = TRUE;
+       /* Generate the mapping pairs array directly into the attr record. */
+       err = ntfs_mapping_pairs_build(vol, (u8*)a +
+                       le16_to_cpu(a->data.non_resident.mapping_pairs_offset),
+                       mp_size, rl2, ll, -1, NULL);
+       if (unlikely(err)) {
+               if (start < 0 || start >= allocated_size)
+                       ntfs_error(vol->sb, "Cannot extend allocation of "
+                                       "inode 0x%lx, attribute type 0x%x, "
+                                       "because building the mapping pairs "
+                                       "failed with error code %i.", vi->i_ino,
+                                       (unsigned)le32_to_cpu(ni->type), err);
+               err = -EIO;
+               goto undo_alloc;
+       }
+       /* Update the highest_vcn. */
+       a->data.non_resident.highest_vcn = cpu_to_sle64((new_alloc_size >>
+                       vol->cluster_size_bits) - 1);
+       /*
+        * We now have extended the allocated size of the attribute.  Reflect
+        * this in the ntfs_inode structure and the attribute record.
+        */
+       if (a->data.non_resident.lowest_vcn) {
+               /*
+                * We are not in the first attribute extent, switch to it, but
+                * first ensure the changes will make it to disk later.
+                */
+               flush_dcache_mft_record_page(ctx->ntfs_ino);
+               mark_mft_record_dirty(ctx->ntfs_ino);
+               ntfs_attr_reinit_search_ctx(ctx);
+               err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
+                               CASE_SENSITIVE, 0, NULL, 0, ctx);
+               if (unlikely(err))
+                       goto restore_undo_alloc;
+               /* @m is not used any more so no need to set it. */
+               a = ctx->attr;
+       }
+       write_lock_irqsave(&ni->size_lock, flags);
+       ni->allocated_size = new_alloc_size;
+       a->data.non_resident.allocated_size = cpu_to_sle64(new_alloc_size);
+       /*
+        * FIXME: This would fail if @ni is a directory, $MFT, or an index,
+        * since those can have sparse/compressed set.  For example can be
+        * set compressed even though it is not compressed itself and in that
+        * case the bit means that files are to be created compressed in the
+        * directory...  At present this is ok as this code is only called for
+        * regular files, and only for their $DATA attribute(s).
+        * FIXME: The calculation is wrong if we created a hole above.  For now
+        * it does not matter as we never create holes.
+        */
+       if (NInoSparse(ni) || NInoCompressed(ni)) {
+               ni->itype.compressed.size += new_alloc_size - allocated_size;
+               a->data.non_resident.compressed_size =
+                               cpu_to_sle64(ni->itype.compressed.size);
+               vi->i_blocks = ni->itype.compressed.size >> 9;
+       } else
+               vi->i_blocks = new_alloc_size >> 9;
+       write_unlock_irqrestore(&ni->size_lock, flags);
+alloc_done:
+       if (new_data_size >= 0) {
+               BUG_ON(new_data_size <
+                               sle64_to_cpu(a->data.non_resident.data_size));
+               a->data.non_resident.data_size = cpu_to_sle64(new_data_size);
+       }
+flush_done:
+       /* Ensure the changes make it to disk. */
+       flush_dcache_mft_record_page(ctx->ntfs_ino);
+       mark_mft_record_dirty(ctx->ntfs_ino);
+done:
+       ntfs_attr_put_search_ctx(ctx);
+       unmap_mft_record(base_ni);
+       up_write(&ni->runlist.lock);
+       ntfs_debug("Done, new_allocated_size 0x%llx.",
+                       (unsigned long long)new_alloc_size);
+       return new_alloc_size;
+restore_undo_alloc:
+       if (start < 0 || start >= allocated_size)
+               ntfs_error(vol->sb, "Cannot complete extension of allocation "
+                               "of inode 0x%lx, attribute type 0x%x, because "
+                               "lookup of first attribute extent failed with "
+                               "error code %i.", vi->i_ino,
+                               (unsigned)le32_to_cpu(ni->type), err);
+       if (err == -ENOENT)
+               err = -EIO;
+       ntfs_attr_reinit_search_ctx(ctx);
+       if (ntfs_attr_lookup(ni->type, ni->name, ni->name_len, CASE_SENSITIVE,
+                       allocated_size >> vol->cluster_size_bits, NULL, 0,
+                       ctx)) {
+               ntfs_error(vol->sb, "Failed to find last attribute extent of "
+                               "attribute in error code path.  Run chkdsk to "
+                               "recover.");
+               write_lock_irqsave(&ni->size_lock, flags);
+               ni->allocated_size = new_alloc_size;
+               /*
+                * FIXME: This would fail if @ni is a directory...  See above.
+                * FIXME: The calculation is wrong if we created a hole above.
+                * For now it does not matter as we never create holes.
+                */
+               if (NInoSparse(ni) || NInoCompressed(ni)) {
+                       ni->itype.compressed.size += new_alloc_size -
+                                       allocated_size;
+                       vi->i_blocks = ni->itype.compressed.size >> 9;
+               } else
+                       vi->i_blocks = new_alloc_size >> 9;
+               write_unlock_irqrestore(&ni->size_lock, flags);
+               ntfs_attr_put_search_ctx(ctx);
+               unmap_mft_record(base_ni);
+               up_write(&ni->runlist.lock);
+               /*
+                * The only thing that is now wrong is the allocated size of the
+                * base attribute extent which chkdsk should be able to fix.
+                */
+               NVolSetErrors(vol);
+               return err;
+       }
+       ctx->attr->data.non_resident.highest_vcn = cpu_to_sle64(
+                       (allocated_size >> vol->cluster_size_bits) - 1);
+undo_alloc:
+       ll = allocated_size >> vol->cluster_size_bits;
+       if (ntfs_cluster_free(ni, ll, -1, ctx) < 0) {
+               ntfs_error(vol->sb, "Failed to release allocated cluster(s) "
+                               "in error code path.  Run chkdsk to recover "
+                               "the lost cluster(s).");
+               NVolSetErrors(vol);
+       }
+       m = ctx->mrec;
+       a = ctx->attr;
+       /*
+        * If the runlist truncation fails and/or the search context is no
+        * longer valid, we cannot resize the attribute record or build the
+        * mapping pairs array thus we mark the inode bad so that no access to
+        * the freed clusters can happen.
+        */
+       if (ntfs_rl_truncate_nolock(vol, &ni->runlist, ll) || IS_ERR(m)) {
+               ntfs_error(vol->sb, "Failed to %s in error code path.  Run "
+                               "chkdsk to recover.", IS_ERR(m) ?
+                               "restore attribute search context" :
+                               "truncate attribute runlist");
+               make_bad_inode(vi);
+               make_bad_inode(VFS_I(base_ni));
+               NVolSetErrors(vol);
+       } else if (mp_rebuilt) {
+               if (ntfs_attr_record_resize(m, a, attr_len)) {
+                       ntfs_error(vol->sb, "Failed to restore attribute "
+                                       "record in error code path.  Run "
+                                       "chkdsk to recover.");
+                       make_bad_inode(vi);
+                       make_bad_inode(VFS_I(base_ni));
+                       NVolSetErrors(vol);
+               } else /* if (success) */ {
+                       if (ntfs_mapping_pairs_build(vol, (u8*)a + le16_to_cpu(
+                                       a->data.non_resident.
+                                       mapping_pairs_offset), attr_len -
+                                       le16_to_cpu(a->data.non_resident.
+                                       mapping_pairs_offset), rl2, ll, -1,
+                                       NULL)) {
+                               ntfs_error(vol->sb, "Failed to restore "
+                                               "mapping pairs array in error "
+                                               "code path.  Run chkdsk to "
+                                               "recover.");
+                               make_bad_inode(vi);
+                               make_bad_inode(VFS_I(base_ni));
+                               NVolSetErrors(vol);
+                       }
+                       flush_dcache_mft_record_page(ctx->ntfs_ino);
+                       mark_mft_record_dirty(ctx->ntfs_ino);
+               }
+       }
+err_out:
+       if (ctx)
+               ntfs_attr_put_search_ctx(ctx);
+       if (m)
+               unmap_mft_record(base_ni);
+       up_write(&ni->runlist.lock);
+conv_err_out:
+       ntfs_debug("Failed.  Returning error code %i.", err);
+       return err;
+}
+
 /**
  * ntfs_attr_set - fill (a part of) an attribute with a byte
  * @ni:                ntfs inode describing the attribute to fill
@@ -1773,6 +2592,8 @@ int ntfs_attr_set(ntfs_inode *ni, const s64 ofs, const s64 cnt, const u8 val)
                /* Finally unlock and release the page. */
                unlock_page(page);
                page_cache_release(page);
+               balance_dirty_pages_ratelimited(mapping);
+               cond_resched();
        }
        /* If there is a last partial page, need to do it the slow way. */
        if (end_ofs) {
index 0618ed6fd7b35de101192db0a058baffd99158cf..9074886b44ba194d1c6cffed6f8bb838f2263f97 100644 (file)
@@ -60,14 +60,15 @@ typedef struct {
        ATTR_RECORD *base_attr;
 } ntfs_attr_search_ctx;
 
-extern int ntfs_map_runlist_nolock(ntfs_inode *ni, VCN vcn);
+extern int ntfs_map_runlist_nolock(ntfs_inode *ni, VCN vcn,
+               ntfs_attr_search_ctx *ctx);
 extern int ntfs_map_runlist(ntfs_inode *ni, VCN vcn);
 
 extern LCN ntfs_attr_vcn_to_lcn_nolock(ntfs_inode *ni, const VCN vcn,
                const BOOL write_locked);
 
 extern runlist_element *ntfs_attr_find_vcn_nolock(ntfs_inode *ni,
-               const VCN vcn, const BOOL write_locked);
+               const VCN vcn, ntfs_attr_search_ctx *ctx);
 
 int ntfs_attr_lookup(const ATTR_TYPE type, const ntfschar *name,
                const u32 name_len, const IGNORE_CASE_BOOL ic,
@@ -102,7 +103,10 @@ extern int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size);
 extern int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a,
                const u32 new_size);
 
-extern int ntfs_attr_make_non_resident(ntfs_inode *ni);
+extern int ntfs_attr_make_non_resident(ntfs_inode *ni, const u32 data_size);
+
+extern s64 ntfs_attr_extend_allocation(ntfs_inode *ni, s64 new_alloc_size,
+               const s64 new_data_size, const s64 data_start);
 
 extern int ntfs_attr_set(ntfs_inode *ni, const s64 ofs, const s64 cnt,
                const u8 val);
index be9fd1dd423d18de8bab0c320f52af5d5ba48392..7275338918132fe31eed51bddf2d48485d52c1b9 100644 (file)
  * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-#include <linux/pagemap.h>
 #include <linux/buffer_head.h>
+#include <linux/pagemap.h>
+#include <linux/pagevec.h>
+#include <linux/sched.h>
+#include <linux/swap.h>
+#include <linux/uio.h>
+#include <linux/writeback.h>
 
+#include <asm/page.h>
+#include <asm/uaccess.h>
+
+#include "attrib.h"
+#include "bitmap.h"
 #include "inode.h"
 #include "debug.h"
+#include "lcnalloc.h"
+#include "malloc.h"
+#include "mft.h"
 #include "ntfs.h"
 
 /**
@@ -55,6 +68,2185 @@ static int ntfs_file_open(struct inode *vi, struct file *filp)
 
 #ifdef NTFS_RW
 
+/**
+ * ntfs_attr_extend_initialized - extend the initialized size of an attribute
+ * @ni:                        ntfs inode of the attribute to extend
+ * @new_init_size:     requested new initialized size in bytes
+ * @cached_page:       store any allocated but unused page here
+ * @lru_pvec:          lru-buffering pagevec of the caller
+ *
+ * Extend the initialized size of an attribute described by the ntfs inode @ni
+ * to @new_init_size bytes.  This involves zeroing any non-sparse space between
+ * the old initialized size and @new_init_size both in the page cache and on
+ * disk (if relevant complete pages are already uptodate in the page cache then
+ * these are simply marked dirty).
+ *
+ * As a side-effect, the file size (vfs inode->i_size) may be incremented as,
+ * in the resident attribute case, it is tied to the initialized size and, in
+ * the non-resident attribute case, it may not fall below the initialized size.
+ *
+ * Note that if the attribute is resident, we do not need to touch the page
+ * cache at all.  This is because if the page cache page is not uptodate we
+ * bring it uptodate later, when doing the write to the mft record since we
+ * then already have the page mapped.  And if the page is uptodate, the
+ * non-initialized region will already have been zeroed when the page was
+ * brought uptodate and the region may in fact already have been overwritten
+ * with new data via mmap() based writes, so we cannot just zero it.  And since
+ * POSIX specifies that the behaviour of resizing a file whilst it is mmap()ped
+ * is unspecified, we choose not to do zeroing and thus we do not need to touch
+ * the page at all.  For a more detailed explanation see ntfs_truncate() in
+ * fs/ntfs/inode.c.
+ *
+ * @cached_page and @lru_pvec are just optimizations for dealing with multiple
+ * pages.
+ *
+ * Return 0 on success and -errno on error.  In the case that an error is
+ * encountered it is possible that the initialized size will already have been
+ * incremented some way towards @new_init_size but it is guaranteed that if
+ * this is the case, the necessary zeroing will also have happened and that all
+ * metadata is self-consistent.
+ *
+ * Locking: i_sem on the vfs inode corrseponsind to the ntfs inode @ni must be
+ *         held by the caller.
+ */
+static int ntfs_attr_extend_initialized(ntfs_inode *ni, const s64 new_init_size,
+               struct page **cached_page, struct pagevec *lru_pvec)
+{
+       s64 old_init_size;
+       loff_t old_i_size;
+       pgoff_t index, end_index;
+       unsigned long flags;
+       struct inode *vi = VFS_I(ni);
+       ntfs_inode *base_ni;
+       MFT_RECORD *m = NULL;
+       ATTR_RECORD *a;
+       ntfs_attr_search_ctx *ctx = NULL;
+       struct address_space *mapping;
+       struct page *page = NULL;
+       u8 *kattr;
+       int err;
+       u32 attr_len;
+
+       read_lock_irqsave(&ni->size_lock, flags);
+       old_init_size = ni->initialized_size;
+       old_i_size = i_size_read(vi);
+       BUG_ON(new_init_size > ni->allocated_size);
+       read_unlock_irqrestore(&ni->size_lock, flags);
+       ntfs_debug("Entering for i_ino 0x%lx, attribute type 0x%x, "
+                       "old_initialized_size 0x%llx, "
+                       "new_initialized_size 0x%llx, i_size 0x%llx.",
+                       vi->i_ino, (unsigned)le32_to_cpu(ni->type),
+                       (unsigned long long)old_init_size,
+                       (unsigned long long)new_init_size, old_i_size);
+       if (!NInoAttr(ni))
+               base_ni = ni;
+       else
+               base_ni = ni->ext.base_ntfs_ino;
+       /* Use goto to reduce indentation and we need the label below anyway. */
+       if (NInoNonResident(ni))
+               goto do_non_resident_extend;
+       BUG_ON(old_init_size != old_i_size);
+       m = map_mft_record(base_ni);
+       if (IS_ERR(m)) {
+               err = PTR_ERR(m);
+               m = NULL;
+               goto err_out;
+       }
+       ctx = ntfs_attr_get_search_ctx(base_ni, m);
+       if (unlikely(!ctx)) {
+               err = -ENOMEM;
+               goto err_out;
+       }
+       err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
+                       CASE_SENSITIVE, 0, NULL, 0, ctx);
+       if (unlikely(err)) {
+               if (err == -ENOENT)
+                       err = -EIO;
+               goto err_out;
+       }
+       m = ctx->mrec;
+       a = ctx->attr;
+       BUG_ON(a->non_resident);
+       /* The total length of the attribute value. */
+       attr_len = le32_to_cpu(a->data.resident.value_length);
+       BUG_ON(old_i_size != (loff_t)attr_len);
+       /*
+        * Do the zeroing in the mft record and update the attribute size in
+        * the mft record.
+        */
+       kattr = (u8*)a + le16_to_cpu(a->data.resident.value_offset);
+       memset(kattr + attr_len, 0, new_init_size - attr_len);
+       a->data.resident.value_length = cpu_to_le32((u32)new_init_size);
+       /* Finally, update the sizes in the vfs and ntfs inodes. */
+       write_lock_irqsave(&ni->size_lock, flags);
+       i_size_write(vi, new_init_size);
+       ni->initialized_size = new_init_size;
+       write_unlock_irqrestore(&ni->size_lock, flags);
+       goto done;
+do_non_resident_extend:
+       /*
+        * If the new initialized size @new_init_size exceeds the current file
+        * size (vfs inode->i_size), we need to extend the file size to the
+        * new initialized size.
+        */
+       if (new_init_size > old_i_size) {
+               m = map_mft_record(base_ni);
+               if (IS_ERR(m)) {
+                       err = PTR_ERR(m);
+                       m = NULL;
+                       goto err_out;
+               }
+               ctx = ntfs_attr_get_search_ctx(base_ni, m);
+               if (unlikely(!ctx)) {
+                       err = -ENOMEM;
+                       goto err_out;
+               }
+               err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
+                               CASE_SENSITIVE, 0, NULL, 0, ctx);
+               if (unlikely(err)) {
+                       if (err == -ENOENT)
+                               err = -EIO;
+                       goto err_out;
+               }
+               m = ctx->mrec;
+               a = ctx->attr;
+               BUG_ON(!a->non_resident);
+               BUG_ON(old_i_size != (loff_t)
+                               sle64_to_cpu(a->data.non_resident.data_size));
+               a->data.non_resident.data_size = cpu_to_sle64(new_init_size);
+               flush_dcache_mft_record_page(ctx->ntfs_ino);
+               mark_mft_record_dirty(ctx->ntfs_ino);
+               /* Update the file size in the vfs inode. */
+               i_size_write(vi, new_init_size);
+               ntfs_attr_put_search_ctx(ctx);
+               ctx = NULL;
+               unmap_mft_record(base_ni);
+               m = NULL;
+       }
+       mapping = vi->i_mapping;
+       index = old_init_size >> PAGE_CACHE_SHIFT;
+       end_index = (new_init_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+       do {
+               /*
+                * Read the page.  If the page is not present, this will zero
+                * the uninitialized regions for us.
+                */
+               page = read_cache_page(mapping, index,
+                               (filler_t*)mapping->a_ops->readpage, NULL);
+               if (IS_ERR(page)) {
+                       err = PTR_ERR(page);
+                       goto init_err_out;
+               }
+               wait_on_page_locked(page);
+               if (unlikely(!PageUptodate(page) || PageError(page))) {
+                       page_cache_release(page);
+                       err = -EIO;
+                       goto init_err_out;
+               }
+               /*
+                * Update the initialized size in the ntfs inode.  This is
+                * enough to make ntfs_writepage() work.
+                */
+               write_lock_irqsave(&ni->size_lock, flags);
+               ni->initialized_size = (index + 1) << PAGE_CACHE_SHIFT;
+               if (ni->initialized_size > new_init_size)
+                       ni->initialized_size = new_init_size;
+               write_unlock_irqrestore(&ni->size_lock, flags);
+               /* Set the page dirty so it gets written out. */
+               set_page_dirty(page);
+               page_cache_release(page);
+               /*
+                * Play nice with the vm and the rest of the system.  This is
+                * very much needed as we can potentially be modifying the
+                * initialised size from a very small value to a really huge
+                * value, e.g.
+                *      f = open(somefile, O_TRUNC);
+                *      truncate(f, 10GiB);
+                *      seek(f, 10GiB);
+                *      write(f, 1);
+                * And this would mean we would be marking dirty hundreds of
+                * thousands of pages or as in the above example more than
+                * two and a half million pages!
+                *
+                * TODO: For sparse pages could optimize this workload by using
+                * the FsMisc / MiscFs page bit as a "PageIsSparse" bit.  This
+                * would be set in readpage for sparse pages and here we would
+                * not need to mark dirty any pages which have this bit set.
+                * The only caveat is that we have to clear the bit everywhere
+                * where we allocate any clusters that lie in the page or that
+                * contain the page.
+                *
+                * TODO: An even greater optimization would be for us to only
+                * call readpage() on pages which are not in sparse regions as
+                * determined from the runlist.  This would greatly reduce the
+                * number of pages we read and make dirty in the case of sparse
+                * files.
+                */
+               balance_dirty_pages_ratelimited(mapping);
+               cond_resched();
+       } while (++index < end_index);
+       read_lock_irqsave(&ni->size_lock, flags);
+       BUG_ON(ni->initialized_size != new_init_size);
+       read_unlock_irqrestore(&ni->size_lock, flags);
+       /* Now bring in sync the initialized_size in the mft record. */
+       m = map_mft_record(base_ni);
+       if (IS_ERR(m)) {
+               err = PTR_ERR(m);
+               m = NULL;
+               goto init_err_out;
+       }
+       ctx = ntfs_attr_get_search_ctx(base_ni, m);
+       if (unlikely(!ctx)) {
+               err = -ENOMEM;
+               goto init_err_out;
+       }
+       err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
+                       CASE_SENSITIVE, 0, NULL, 0, ctx);
+       if (unlikely(err)) {
+               if (err == -ENOENT)
+                       err = -EIO;
+               goto init_err_out;
+       }
+       m = ctx->mrec;
+       a = ctx->attr;
+       BUG_ON(!a->non_resident);
+       a->data.non_resident.initialized_size = cpu_to_sle64(new_init_size);
+done:
+       flush_dcache_mft_record_page(ctx->ntfs_ino);
+       mark_mft_record_dirty(ctx->ntfs_ino);
+       if (ctx)
+               ntfs_attr_put_search_ctx(ctx);
+       if (m)
+               unmap_mft_record(base_ni);
+       ntfs_debug("Done, initialized_size 0x%llx, i_size 0x%llx.",
+                       (unsigned long long)new_init_size, i_size_read(vi));
+       return 0;
+init_err_out:
+       write_lock_irqsave(&ni->size_lock, flags);
+       ni->initialized_size = old_init_size;
+       write_unlock_irqrestore(&ni->size_lock, flags);
+err_out:
+       if (ctx)
+               ntfs_attr_put_search_ctx(ctx);
+       if (m)
+               unmap_mft_record(base_ni);
+       ntfs_debug("Failed.  Returning error code %i.", err);
+       return err;
+}
+
+/**
+ * ntfs_fault_in_pages_readable -
+ *
+ * Fault a number of userspace pages into pagetables.
+ *
+ * Unlike include/linux/pagemap.h::fault_in_pages_readable(), this one copes
+ * with more than two userspace pages as well as handling the single page case
+ * elegantly.
+ *
+ * If you find this difficult to understand, then think of the while loop being
+ * the following code, except that we do without the integer variable ret:
+ *
+ *     do {
+ *             ret = __get_user(c, uaddr);
+ *             uaddr += PAGE_SIZE;
+ *     } while (!ret && uaddr < end);
+ *
+ * Note, the final __get_user() may well run out-of-bounds of the user buffer,
+ * but _not_ out-of-bounds of the page the user buffer belongs to, and since
+ * this is only a read and not a write, and since it is still in the same page,
+ * it should not matter and this makes the code much simpler.
+ */
+static inline void ntfs_fault_in_pages_readable(const char __user *uaddr,
+               int bytes)
+{
+       const char __user *end;
+       volatile char c;
+
+       /* Set @end to the first byte outside the last page we care about. */
+       end = (const char __user*)PAGE_ALIGN((ptrdiff_t __user)uaddr + bytes);
+
+       while (!__get_user(c, uaddr) && (uaddr += PAGE_SIZE, uaddr < end))
+               ;
+}
+
+/**
+ * ntfs_fault_in_pages_readable_iovec -
+ *
+ * Same as ntfs_fault_in_pages_readable() but operates on an array of iovecs.
+ */
+static inline void ntfs_fault_in_pages_readable_iovec(const struct iovec *iov,
+               size_t iov_ofs, int bytes)
+{
+       do {
+               const char __user *buf;
+               unsigned len;
+
+               buf = iov->iov_base + iov_ofs;
+               len = iov->iov_len - iov_ofs;
+               if (len > bytes)
+                       len = bytes;
+               ntfs_fault_in_pages_readable(buf, len);
+               bytes -= len;
+               iov++;
+               iov_ofs = 0;
+       } while (bytes);
+}
+
+/**
+ * __ntfs_grab_cache_pages - obtain a number of locked pages
+ * @mapping:   address space mapping from which to obtain page cache pages
+ * @index:     starting index in @mapping at which to begin obtaining pages
+ * @nr_pages:  number of page cache pages to obtain
+ * @pages:     array of pages in which to return the obtained page cache pages
+ * @cached_page: allocated but as yet unused page
+ * @lru_pvec:  lru-buffering pagevec of caller
+ *
+ * Obtain @nr_pages locked page cache pages from the mapping @maping and
+ * starting at index @index.
+ *
+ * If a page is newly created, increment its refcount and add it to the
+ * caller's lru-buffering pagevec @lru_pvec.
+ *
+ * This is the same as mm/filemap.c::__grab_cache_page(), except that @nr_pages
+ * are obtained at once instead of just one page and that 0 is returned on
+ * success and -errno on error.
+ *
+ * Note, the page locks are obtained in ascending page index order.
+ */
+static inline int __ntfs_grab_cache_pages(struct address_space *mapping,
+               pgoff_t index, const unsigned nr_pages, struct page **pages,
+               struct page **cached_page, struct pagevec *lru_pvec)
+{
+       int err, nr;
+
+       BUG_ON(!nr_pages);
+       err = nr = 0;
+       do {
+               pages[nr] = find_lock_page(mapping, index);
+               if (!pages[nr]) {
+                       if (!*cached_page) {
+                               *cached_page = page_cache_alloc(mapping);
+                               if (unlikely(!*cached_page)) {
+                                       err = -ENOMEM;
+                                       goto err_out;
+                               }
+                       }
+                       err = add_to_page_cache(*cached_page, mapping, index,
+                                       GFP_KERNEL);
+                       if (unlikely(err)) {
+                               if (err == -EEXIST)
+                                       continue;
+                               goto err_out;
+                       }
+                       pages[nr] = *cached_page;
+                       page_cache_get(*cached_page);
+                       if (unlikely(!pagevec_add(lru_pvec, *cached_page)))
+                               __pagevec_lru_add(lru_pvec);
+                       *cached_page = NULL;
+               }
+               index++;
+               nr++;
+       } while (nr < nr_pages);
+out:
+       return err;
+err_out:
+       while (nr > 0) {
+               unlock_page(pages[--nr]);
+               page_cache_release(pages[nr]);
+       }
+       goto out;
+}
+
+static inline int ntfs_submit_bh_for_read(struct buffer_head *bh)
+{
+       lock_buffer(bh);
+       get_bh(bh);
+       bh->b_end_io = end_buffer_read_sync;
+       return submit_bh(READ, bh);
+}
+
+/**
+ * ntfs_prepare_pages_for_non_resident_write - prepare pages for receiving data
+ * @pages:     array of destination pages
+ * @nr_pages:  number of pages in @pages
+ * @pos:       byte position in file at which the write begins
+ * @bytes:     number of bytes to be written
+ *
+ * This is called for non-resident attributes from ntfs_file_buffered_write()
+ * with i_sem held on the inode (@pages[0]->mapping->host).  There are
+ * @nr_pages pages in @pages which are locked but not kmap()ped.  The source
+ * data has not yet been copied into the @pages.
+ * 
+ * Need to fill any holes with actual clusters, allocate buffers if necessary,
+ * ensure all the buffers are mapped, and bring uptodate any buffers that are
+ * only partially being written to.
+ *
+ * If @nr_pages is greater than one, we are guaranteed that the cluster size is
+ * greater than PAGE_CACHE_SIZE, that all pages in @pages are entirely inside
+ * the same cluster and that they are the entirety of that cluster, and that
+ * the cluster is sparse, i.e. we need to allocate a cluster to fill the hole.
+ *
+ * i_size is not to be modified yet.
+ *
+ * Return 0 on success or -errno on error.
+ */
+static int ntfs_prepare_pages_for_non_resident_write(struct page **pages,
+               unsigned nr_pages, s64 pos, size_t bytes)
+{
+       VCN vcn, highest_vcn = 0, cpos, cend, bh_cpos, bh_cend;
+       LCN lcn;
+       s64 bh_pos, vcn_len, end, initialized_size;
+       sector_t lcn_block;
+       struct page *page;
+       struct inode *vi;
+       ntfs_inode *ni, *base_ni = NULL;
+       ntfs_volume *vol;
+       runlist_element *rl, *rl2;
+       struct buffer_head *bh, *head, *wait[2], **wait_bh = wait;
+       ntfs_attr_search_ctx *ctx = NULL;
+       MFT_RECORD *m = NULL;
+       ATTR_RECORD *a = NULL;
+       unsigned long flags;
+       u32 attr_rec_len = 0;
+       unsigned blocksize, u;
+       int err, mp_size;
+       BOOL rl_write_locked, was_hole, is_retry;
+       unsigned char blocksize_bits;
+       struct {
+               u8 runlist_merged:1;
+               u8 mft_attr_mapped:1;
+               u8 mp_rebuilt:1;
+               u8 attr_switched:1;
+       } status = { 0, 0, 0, 0 };
+
+       BUG_ON(!nr_pages);
+       BUG_ON(!pages);
+       BUG_ON(!*pages);
+       vi = pages[0]->mapping->host;
+       ni = NTFS_I(vi);
+       vol = ni->vol;
+       ntfs_debug("Entering for inode 0x%lx, attribute type 0x%x, start page "
+                       "index 0x%lx, nr_pages 0x%x, pos 0x%llx, bytes 0x%zx.",
+                       vi->i_ino, ni->type, pages[0]->index, nr_pages,
+                       (long long)pos, bytes);
+       blocksize_bits = vi->i_blkbits;
+       blocksize = 1 << blocksize_bits;
+       u = 0;
+       do {
+               struct page *page = pages[u];
+               /*
+                * create_empty_buffers() will create uptodate/dirty buffers if
+                * the page is uptodate/dirty.
+                */
+               if (!page_has_buffers(page)) {
+                       create_empty_buffers(page, blocksize, 0);
+                       if (unlikely(!page_has_buffers(page)))
+                               return -ENOMEM;
+               }
+       } while (++u < nr_pages);
+       rl_write_locked = FALSE;
+       rl = NULL;
+       err = 0;
+       vcn = lcn = -1;
+       vcn_len = 0;
+       lcn_block = -1;
+       was_hole = FALSE;
+       cpos = pos >> vol->cluster_size_bits;
+       end = pos + bytes;
+       cend = (end + vol->cluster_size - 1) >> vol->cluster_size_bits;
+       /*
+        * Loop over each page and for each page over each buffer.  Use goto to
+        * reduce indentation.
+        */
+       u = 0;
+do_next_page:
+       page = pages[u];
+       bh_pos = (s64)page->index << PAGE_CACHE_SHIFT;
+       bh = head = page_buffers(page);
+       do {
+               VCN cdelta;
+               s64 bh_end;
+               unsigned bh_cofs;
+
+               /* Clear buffer_new on all buffers to reinitialise state. */
+               if (buffer_new(bh))
+                       clear_buffer_new(bh);
+               bh_end = bh_pos + blocksize;
+               bh_cpos = bh_pos >> vol->cluster_size_bits;
+               bh_cofs = bh_pos & vol->cluster_size_mask;
+               if (buffer_mapped(bh)) {
+                       /*
+                        * The buffer is already mapped.  If it is uptodate,
+                        * ignore it.
+                        */
+                       if (buffer_uptodate(bh))
+                               continue;
+                       /*
+                        * The buffer is not uptodate.  If the page is uptodate
+                        * set the buffer uptodate and otherwise ignore it.
+                        */
+                       if (PageUptodate(page)) {
+                               set_buffer_uptodate(bh);
+                               continue;
+                       }
+                       /*
+                        * Neither the page nor the buffer are uptodate.  If
+                        * the buffer is only partially being written to, we
+                        * need to read it in before the write, i.e. now.
+                        */
+                       if ((bh_pos < pos && bh_end > pos) ||
+                                       (bh_pos < end && bh_end > end)) {
+                               /*
+                                * If the buffer is fully or partially within
+                                * the initialized size, do an actual read.
+                                * Otherwise, simply zero the buffer.
+                                */
+                               read_lock_irqsave(&ni->size_lock, flags);
+                               initialized_size = ni->initialized_size;
+                               read_unlock_irqrestore(&ni->size_lock, flags);
+                               if (bh_pos < initialized_size) {
+                                       ntfs_submit_bh_for_read(bh);
+                                       *wait_bh++ = bh;
+                               } else {
+                                       u8 *kaddr = kmap_atomic(page, KM_USER0);
+                                       memset(kaddr + bh_offset(bh), 0,
+                                                       blocksize);
+                                       kunmap_atomic(kaddr, KM_USER0);
+                                       flush_dcache_page(page);
+                                       set_buffer_uptodate(bh);
+                               }
+                       }
+                       continue;
+               }
+               /* Unmapped buffer.  Need to map it. */
+               bh->b_bdev = vol->sb->s_bdev;
+               /*
+                * If the current buffer is in the same clusters as the map
+                * cache, there is no need to check the runlist again.  The
+                * map cache is made up of @vcn, which is the first cached file
+                * cluster, @vcn_len which is the number of cached file
+                * clusters, @lcn is the device cluster corresponding to @vcn,
+                * and @lcn_block is the block number corresponding to @lcn.
+                */
+               cdelta = bh_cpos - vcn;
+               if (likely(!cdelta || (cdelta > 0 && cdelta < vcn_len))) {
+map_buffer_cached:
+                       BUG_ON(lcn < 0);
+                       bh->b_blocknr = lcn_block +
+                                       (cdelta << (vol->cluster_size_bits -
+                                       blocksize_bits)) +
+                                       (bh_cofs >> blocksize_bits);
+                       set_buffer_mapped(bh);
+                       /*
+                        * If the page is uptodate so is the buffer.  If the
+                        * buffer is fully outside the write, we ignore it if
+                        * it was already allocated and we mark it dirty so it
+                        * gets written out if we allocated it.  On the other
+                        * hand, if we allocated the buffer but we are not
+                        * marking it dirty we set buffer_new so we can do
+                        * error recovery.
+                        */
+                       if (PageUptodate(page)) {
+                               if (!buffer_uptodate(bh))
+                                       set_buffer_uptodate(bh);
+                               if (unlikely(was_hole)) {
+                                       /* We allocated the buffer. */
+                                       unmap_underlying_metadata(bh->b_bdev,
+                                                       bh->b_blocknr);
+                                       if (bh_end <= pos || bh_pos >= end)
+                                               mark_buffer_dirty(bh);
+                                       else
+                                               set_buffer_new(bh);
+                               }
+                               continue;
+                       }
+                       /* Page is _not_ uptodate. */
+                       if (likely(!was_hole)) {
+                               /*
+                                * Buffer was already allocated.  If it is not
+                                * uptodate and is only partially being written
+                                * to, we need to read it in before the write,
+                                * i.e. now.
+                                */
+                               if (!buffer_uptodate(bh) && bh_pos < end &&
+                                               bh_end > pos &&
+                                               (bh_pos < pos ||
+                                               bh_end > end)) {
+                                       /*
+                                        * If the buffer is fully or partially
+                                        * within the initialized size, do an
+                                        * actual read.  Otherwise, simply zero
+                                        * the buffer.
+                                        */
+                                       read_lock_irqsave(&ni->size_lock,
+                                                       flags);
+                                       initialized_size = ni->initialized_size;
+                                       read_unlock_irqrestore(&ni->size_lock,
+                                                       flags);
+                                       if (bh_pos < initialized_size) {
+                                               ntfs_submit_bh_for_read(bh);
+                                               *wait_bh++ = bh;
+                                       } else {
+                                               u8 *kaddr = kmap_atomic(page,
+                                                               KM_USER0);
+                                               memset(kaddr + bh_offset(bh),
+                                                               0, blocksize);
+                                               kunmap_atomic(kaddr, KM_USER0);
+                                               flush_dcache_page(page);
+                                               set_buffer_uptodate(bh);
+                                       }
+                               }
+                               continue;
+                       }
+                       /* We allocated the buffer. */
+                       unmap_underlying_metadata(bh->b_bdev, bh->b_blocknr);
+                       /*
+                        * If the buffer is fully outside the write, zero it,
+                        * set it uptodate, and mark it dirty so it gets
+                        * written out.  If it is partially being written to,
+                        * zero region surrounding the write but leave it to
+                        * commit write to do anything else.  Finally, if the
+                        * buffer is fully being overwritten, do nothing.
+                        */
+                       if (bh_end <= pos || bh_pos >= end) {
+                               if (!buffer_uptodate(bh)) {
+                                       u8 *kaddr = kmap_atomic(page, KM_USER0);
+                                       memset(kaddr + bh_offset(bh), 0,
+                                                       blocksize);
+                                       kunmap_atomic(kaddr, KM_USER0);
+                                       flush_dcache_page(page);
+                                       set_buffer_uptodate(bh);
+                               }
+                               mark_buffer_dirty(bh);
+                               continue;
+                       }
+                       set_buffer_new(bh);
+                       if (!buffer_uptodate(bh) &&
+                                       (bh_pos < pos || bh_end > end)) {
+                               u8 *kaddr;
+                               unsigned pofs;
+                                       
+                               kaddr = kmap_atomic(page, KM_USER0);
+                               if (bh_pos < pos) {
+                                       pofs = bh_pos & ~PAGE_CACHE_MASK;
+                                       memset(kaddr + pofs, 0, pos - bh_pos);
+                               }
+                               if (bh_end > end) {
+                                       pofs = end & ~PAGE_CACHE_MASK;
+                                       memset(kaddr + pofs, 0, bh_end - end);
+                               }
+                               kunmap_atomic(kaddr, KM_USER0);
+                               flush_dcache_page(page);
+                       }
+                       continue;
+               }
+               /*
+                * Slow path: this is the first buffer in the cluster.  If it
+                * is outside allocated size and is not uptodate, zero it and
+                * set it uptodate.
+                */
+               read_lock_irqsave(&ni->size_lock, flags);
+               initialized_size = ni->allocated_size;
+               read_unlock_irqrestore(&ni->size_lock, flags);
+               if (bh_pos > initialized_size) {
+                       if (PageUptodate(page)) {
+                               if (!buffer_uptodate(bh))
+                                       set_buffer_uptodate(bh);
+                       } else if (!buffer_uptodate(bh)) {
+                               u8 *kaddr = kmap_atomic(page, KM_USER0);
+                               memset(kaddr + bh_offset(bh), 0, blocksize);
+                               kunmap_atomic(kaddr, KM_USER0);
+                               flush_dcache_page(page);
+                               set_buffer_uptodate(bh);
+                       }
+                       continue;
+               }
+               is_retry = FALSE;
+               if (!rl) {
+                       down_read(&ni->runlist.lock);
+retry_remap:
+                       rl = ni->runlist.rl;
+               }
+               if (likely(rl != NULL)) {
+                       /* Seek to element containing target cluster. */
+                       while (rl->length && rl[1].vcn <= bh_cpos)
+                               rl++;
+                       lcn = ntfs_rl_vcn_to_lcn(rl, bh_cpos);
+                       if (likely(lcn >= 0)) {
+                               /*
+                                * Successful remap, setup the map cache and
+                                * use that to deal with the buffer.
+                                */
+                               was_hole = FALSE;
+                               vcn = bh_cpos;
+                               vcn_len = rl[1].vcn - vcn;
+                               lcn_block = lcn << (vol->cluster_size_bits -
+                                               blocksize_bits);
+                               cdelta = 0;
+                               /*
+                                * If the number of remaining clusters touched
+                                * by the write is smaller or equal to the
+                                * number of cached clusters, unlock the
+                                * runlist as the map cache will be used from
+                                * now on.
+                                */
+                               if (likely(vcn + vcn_len >= cend)) {
+                                       if (rl_write_locked) {
+                                               up_write(&ni->runlist.lock);
+                                               rl_write_locked = FALSE;
+                                       } else
+                                               up_read(&ni->runlist.lock);
+                                       rl = NULL;
+                               }
+                               goto map_buffer_cached;
+                       }
+               } else
+                       lcn = LCN_RL_NOT_MAPPED;
+               /*
+                * If it is not a hole and not out of bounds, the runlist is
+                * probably unmapped so try to map it now.
+                */
+               if (unlikely(lcn != LCN_HOLE && lcn != LCN_ENOENT)) {
+                       if (likely(!is_retry && lcn == LCN_RL_NOT_MAPPED)) {
+                               /* Attempt to map runlist. */
+                               if (!rl_write_locked) {
+                                       /*
+                                        * We need the runlist locked for
+                                        * writing, so if it is locked for
+                                        * reading relock it now and retry in
+                                        * case it changed whilst we dropped
+                                        * the lock.
+                                        */
+                                       up_read(&ni->runlist.lock);
+                                       down_write(&ni->runlist.lock);
+                                       rl_write_locked = TRUE;
+                                       goto retry_remap;
+                               }
+                               err = ntfs_map_runlist_nolock(ni, bh_cpos,
+                                               NULL);
+                               if (likely(!err)) {
+                                       is_retry = TRUE;
+                                       goto retry_remap;
+                               }
+                               /*
+                                * If @vcn is out of bounds, pretend @lcn is
+                                * LCN_ENOENT.  As long as the buffer is out
+                                * of bounds this will work fine.
+                                */
+                               if (err == -ENOENT) {
+                                       lcn = LCN_ENOENT;
+                                       err = 0;
+                                       goto rl_not_mapped_enoent;
+                               }
+                       } else
+                               err = -EIO;
+                       /* Failed to map the buffer, even after retrying. */
+                       bh->b_blocknr = -1;
+                       ntfs_error(vol->sb, "Failed to write to inode 0x%lx, "
+                                       "attribute type 0x%x, vcn 0x%llx, "
+                                       "vcn offset 0x%x, because its "
+                                       "location on disk could not be "
+                                       "determined%s (error code %i).",
+                                       ni->mft_no, ni->type,
+                                       (unsigned long long)bh_cpos,
+                                       (unsigned)bh_pos &
+                                       vol->cluster_size_mask,
+                                       is_retry ? " even after retrying" : "",
+                                       err);
+                       break;
+               }
+rl_not_mapped_enoent:
+               /*
+                * The buffer is in a hole or out of bounds.  We need to fill
+                * the hole, unless the buffer is in a cluster which is not
+                * touched by the write, in which case we just leave the buffer
+                * unmapped.  This can only happen when the cluster size is
+                * less than the page cache size.
+                */
+               if (unlikely(vol->cluster_size < PAGE_CACHE_SIZE)) {
+                       bh_cend = (bh_end + vol->cluster_size - 1) >>
+                                       vol->cluster_size_bits;
+                       if ((bh_cend <= cpos || bh_cpos >= cend)) {
+                               bh->b_blocknr = -1;
+                               /*
+                                * If the buffer is uptodate we skip it.  If it
+                                * is not but the page is uptodate, we can set
+                                * the buffer uptodate.  If the page is not
+                                * uptodate, we can clear the buffer and set it
+                                * uptodate.  Whether this is worthwhile is
+                                * debatable and this could be removed.
+                                */
+                               if (PageUptodate(page)) {
+                                       if (!buffer_uptodate(bh))
+                                               set_buffer_uptodate(bh);
+                               } else if (!buffer_uptodate(bh)) {
+                                       u8 *kaddr = kmap_atomic(page, KM_USER0);
+                                       memset(kaddr + bh_offset(bh), 0,
+                                                       blocksize);
+                                       kunmap_atomic(kaddr, KM_USER0);
+                                       flush_dcache_page(page);
+                                       set_buffer_uptodate(bh);
+                               }
+                               continue;
+                       }
+               }
+               /*
+                * Out of bounds buffer is invalid if it was not really out of
+                * bounds.
+                */
+               BUG_ON(lcn != LCN_HOLE);
+               /*
+                * We need the runlist locked for writing, so if it is locked
+                * for reading relock it now and retry in case it changed
+                * whilst we dropped the lock.
+                */
+               BUG_ON(!rl);
+               if (!rl_write_locked) {
+                       up_read(&ni->runlist.lock);
+                       down_write(&ni->runlist.lock);
+                       rl_write_locked = TRUE;
+                       goto retry_remap;
+               }
+               /* Find the previous last allocated cluster. */
+               BUG_ON(rl->lcn != LCN_HOLE);
+               lcn = -1;
+               rl2 = rl;
+               while (--rl2 >= ni->runlist.rl) {
+                       if (rl2->lcn >= 0) {
+                               lcn = rl2->lcn + rl2->length;
+                               break;
+                       }
+               }
+               rl2 = ntfs_cluster_alloc(vol, bh_cpos, 1, lcn, DATA_ZONE,
+                               FALSE);
+               if (IS_ERR(rl2)) {
+                       err = PTR_ERR(rl2);
+                       ntfs_debug("Failed to allocate cluster, error code %i.",
+                                       err);
+                       break;
+               }
+               lcn = rl2->lcn;
+               rl = ntfs_runlists_merge(ni->runlist.rl, rl2);
+               if (IS_ERR(rl)) {
+                       err = PTR_ERR(rl);
+                       if (err != -ENOMEM)
+                               err = -EIO;
+                       if (ntfs_cluster_free_from_rl(vol, rl2)) {
+                               ntfs_error(vol->sb, "Failed to release "
+                                               "allocated cluster in error "
+                                               "code path.  Run chkdsk to "
+                                               "recover the lost cluster.");
+                               NVolSetErrors(vol);
+                       }
+                       ntfs_free(rl2);
+                       break;
+               }
+               ni->runlist.rl = rl;
+               status.runlist_merged = 1;
+               ntfs_debug("Allocated cluster, lcn 0x%llx.", lcn);
+               /* Map and lock the mft record and get the attribute record. */
+               if (!NInoAttr(ni))
+                       base_ni = ni;
+               else
+                       base_ni = ni->ext.base_ntfs_ino;
+               m = map_mft_record(base_ni);
+               if (IS_ERR(m)) {
+                       err = PTR_ERR(m);
+                       break;
+               }
+               ctx = ntfs_attr_get_search_ctx(base_ni, m);
+               if (unlikely(!ctx)) {
+                       err = -ENOMEM;
+                       unmap_mft_record(base_ni);
+                       break;
+               }
+               status.mft_attr_mapped = 1;
+               err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
+                               CASE_SENSITIVE, bh_cpos, NULL, 0, ctx);
+               if (unlikely(err)) {
+                       if (err == -ENOENT)
+                               err = -EIO;
+                       break;
+               }
+               m = ctx->mrec;
+               a = ctx->attr;
+               /*
+                * Find the runlist element with which the attribute extent
+                * starts.  Note, we cannot use the _attr_ version because we
+                * have mapped the mft record.  That is ok because we know the
+                * runlist fragment must be mapped already to have ever gotten
+                * here, so we can just use the _rl_ version.
+                */
+               vcn = sle64_to_cpu(a->data.non_resident.lowest_vcn);
+               rl2 = ntfs_rl_find_vcn_nolock(rl, vcn);
+               BUG_ON(!rl2);
+               BUG_ON(!rl2->length);
+               BUG_ON(rl2->lcn < LCN_HOLE);
+               highest_vcn = sle64_to_cpu(a->data.non_resident.highest_vcn);
+               /*
+                * If @highest_vcn is zero, calculate the real highest_vcn
+                * (which can really be zero).
+                */
+               if (!highest_vcn)
+                       highest_vcn = (sle64_to_cpu(
+                                       a->data.non_resident.allocated_size) >>
+                                       vol->cluster_size_bits) - 1;
+               /*
+                * Determine the size of the mapping pairs array for the new
+                * extent, i.e. the old extent with the hole filled.
+                */
+               mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, vcn,
+                               highest_vcn);
+               if (unlikely(mp_size <= 0)) {
+                       if (!(err = mp_size))
+                               err = -EIO;
+                       ntfs_debug("Failed to get size for mapping pairs "
+                                       "array, error code %i.", err);
+                       break;
+               }
+               /*
+                * Resize the attribute record to fit the new mapping pairs
+                * array.
+                */
+               attr_rec_len = le32_to_cpu(a->length);
+               err = ntfs_attr_record_resize(m, a, mp_size + le16_to_cpu(
+                               a->data.non_resident.mapping_pairs_offset));
+               if (unlikely(err)) {
+                       BUG_ON(err != -ENOSPC);
+                       // TODO: Deal with this by using the current attribute
+                       // and fill it with as much of the mapping pairs
+                       // array as possible.  Then loop over each attribute
+                       // extent rewriting the mapping pairs arrays as we go
+                       // along and if when we reach the end we have not
+                       // enough space, try to resize the last attribute
+                       // extent and if even that fails, add a new attribute
+                       // extent.
+                       // We could also try to resize at each step in the hope
+                       // that we will not need to rewrite every single extent.
+                       // Note, we may need to decompress some extents to fill
+                       // the runlist as we are walking the extents...
+                       ntfs_error(vol->sb, "Not enough space in the mft "
+                                       "record for the extended attribute "
+                                       "record.  This case is not "
+                                       "implemented yet.");
+                       err = -EOPNOTSUPP;
+                       break ;
+               }
+               status.mp_rebuilt = 1;
+               /*
+                * Generate the mapping pairs array directly into the attribute
+                * record.
+                */
+               err = ntfs_mapping_pairs_build(vol, (u8*)a + le16_to_cpu(
+                               a->data.non_resident.mapping_pairs_offset),
+                               mp_size, rl2, vcn, highest_vcn, NULL);
+               if (unlikely(err)) {
+                       ntfs_error(vol->sb, "Cannot fill hole in inode 0x%lx, "
+                                       "attribute type 0x%x, because building "
+                                       "the mapping pairs failed with error "
+                                       "code %i.", vi->i_ino,
+                                       (unsigned)le32_to_cpu(ni->type), err);
+                       err = -EIO;
+                       break;
+               }
+               /* Update the highest_vcn but only if it was not set. */
+               if (unlikely(!a->data.non_resident.highest_vcn))
+                       a->data.non_resident.highest_vcn =
+                                       cpu_to_sle64(highest_vcn);
+               /*
+                * If the attribute is sparse/compressed, update the compressed
+                * size in the ntfs_inode structure and the attribute record.
+                */
+               if (likely(NInoSparse(ni) || NInoCompressed(ni))) {
+                       /*
+                        * If we are not in the first attribute extent, switch
+                        * to it, but first ensure the changes will make it to
+                        * disk later.
+                        */
+                       if (a->data.non_resident.lowest_vcn) {
+                               flush_dcache_mft_record_page(ctx->ntfs_ino);
+                               mark_mft_record_dirty(ctx->ntfs_ino);
+                               ntfs_attr_reinit_search_ctx(ctx);
+                               err = ntfs_attr_lookup(ni->type, ni->name,
+                                               ni->name_len, CASE_SENSITIVE,
+                                               0, NULL, 0, ctx);
+                               if (unlikely(err)) {
+                                       status.attr_switched = 1;
+                                       break;
+                               }
+                               /* @m is not used any more so do not set it. */
+                               a = ctx->attr;
+                       }
+                       write_lock_irqsave(&ni->size_lock, flags);
+                       ni->itype.compressed.size += vol->cluster_size;
+                       a->data.non_resident.compressed_size =
+                                       cpu_to_sle64(ni->itype.compressed.size);
+                       write_unlock_irqrestore(&ni->size_lock, flags);
+               }
+               /* Ensure the changes make it to disk. */
+               flush_dcache_mft_record_page(ctx->ntfs_ino);
+               mark_mft_record_dirty(ctx->ntfs_ino);
+               ntfs_attr_put_search_ctx(ctx);
+               unmap_mft_record(base_ni);
+               /* Successfully filled the hole. */
+               status.runlist_merged = 0;
+               status.mft_attr_mapped = 0;
+               status.mp_rebuilt = 0;
+               /* Setup the map cache and use that to deal with the buffer. */
+               was_hole = TRUE;
+               vcn = bh_cpos;
+               vcn_len = 1;
+               lcn_block = lcn << (vol->cluster_size_bits - blocksize_bits);
+               cdelta = 0;
+               /*
+                * If the number of remaining clusters in the @pages is smaller
+                * or equal to the number of cached clusters, unlock the
+                * runlist as the map cache will be used from now on.
+                */
+               if (likely(vcn + vcn_len >= cend)) {
+                       up_write(&ni->runlist.lock);
+                       rl_write_locked = FALSE;
+                       rl = NULL;
+               }
+               goto map_buffer_cached;
+       } while (bh_pos += blocksize, (bh = bh->b_this_page) != head);
+       /* If there are no errors, do the next page. */
+       if (likely(!err && ++u < nr_pages))
+               goto do_next_page;
+       /* If there are no errors, release the runlist lock if we took it. */
+       if (likely(!err)) {
+               if (unlikely(rl_write_locked)) {
+                       up_write(&ni->runlist.lock);
+                       rl_write_locked = FALSE;
+               } else if (unlikely(rl))
+                       up_read(&ni->runlist.lock);
+               rl = NULL;
+       }
+       /* If we issued read requests, let them complete. */
+       read_lock_irqsave(&ni->size_lock, flags);
+       initialized_size = ni->initialized_size;
+       read_unlock_irqrestore(&ni->size_lock, flags);
+       while (wait_bh > wait) {
+               bh = *--wait_bh;
+               wait_on_buffer(bh);
+               if (likely(buffer_uptodate(bh))) {
+                       page = bh->b_page;
+                       bh_pos = ((s64)page->index << PAGE_CACHE_SHIFT) +
+                                       bh_offset(bh);
+                       /*
+                        * If the buffer overflows the initialized size, need
+                        * to zero the overflowing region.
+                        */
+                       if (unlikely(bh_pos + blocksize > initialized_size)) {
+                               u8 *kaddr;
+                               int ofs = 0;
+
+                               if (likely(bh_pos < initialized_size))
+                                       ofs = initialized_size - bh_pos;
+                               kaddr = kmap_atomic(page, KM_USER0);
+                               memset(kaddr + bh_offset(bh) + ofs, 0,
+                                               blocksize - ofs);
+                               kunmap_atomic(kaddr, KM_USER0);
+                               flush_dcache_page(page);
+                       }
+               } else /* if (unlikely(!buffer_uptodate(bh))) */
+                       err = -EIO;
+       }
+       if (likely(!err)) {
+               /* Clear buffer_new on all buffers. */
+               u = 0;
+               do {
+                       bh = head = page_buffers(pages[u]);
+                       do {
+                               if (buffer_new(bh))
+                                       clear_buffer_new(bh);
+                       } while ((bh = bh->b_this_page) != head);
+               } while (++u < nr_pages);
+               ntfs_debug("Done.");
+               return err;
+       }
+       if (status.attr_switched) {
+               /* Get back to the attribute extent we modified. */
+               ntfs_attr_reinit_search_ctx(ctx);
+               if (ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
+                               CASE_SENSITIVE, bh_cpos, NULL, 0, ctx)) {
+                       ntfs_error(vol->sb, "Failed to find required "
+                                       "attribute extent of attribute in "
+                                       "error code path.  Run chkdsk to "
+                                       "recover.");
+                       write_lock_irqsave(&ni->size_lock, flags);
+                       ni->itype.compressed.size += vol->cluster_size;
+                       write_unlock_irqrestore(&ni->size_lock, flags);
+                       flush_dcache_mft_record_page(ctx->ntfs_ino);
+                       mark_mft_record_dirty(ctx->ntfs_ino);
+                       /*
+                        * The only thing that is now wrong is the compressed
+                        * size of the base attribute extent which chkdsk
+                        * should be able to fix.
+                        */
+                       NVolSetErrors(vol);
+               } else {
+                       m = ctx->mrec;
+                       a = ctx->attr;
+                       status.attr_switched = 0;
+               }
+       }
+       /*
+        * If the runlist has been modified, need to restore it by punching a
+        * hole into it and we then need to deallocate the on-disk cluster as
+        * well.  Note, we only modify the runlist if we are able to generate a
+        * new mapping pairs array, i.e. only when the mapped attribute extent
+        * is not switched.
+        */
+       if (status.runlist_merged && !status.attr_switched) {
+               BUG_ON(!rl_write_locked);
+               /* Make the file cluster we allocated sparse in the runlist. */
+               if (ntfs_rl_punch_nolock(vol, &ni->runlist, bh_cpos, 1)) {
+                       ntfs_error(vol->sb, "Failed to punch hole into "
+                                       "attribute runlist in error code "
+                                       "path.  Run chkdsk to recover the "
+                                       "lost cluster.");
+                       make_bad_inode(vi);
+                       make_bad_inode(VFS_I(base_ni));
+                       NVolSetErrors(vol);
+               } else /* if (success) */ {
+                       status.runlist_merged = 0;
+                       /*
+                        * Deallocate the on-disk cluster we allocated but only
+                        * if we succeeded in punching its vcn out of the
+                        * runlist.
+                        */
+                       down_write(&vol->lcnbmp_lock);
+                       if (ntfs_bitmap_clear_bit(vol->lcnbmp_ino, lcn)) {
+                               ntfs_error(vol->sb, "Failed to release "
+                                               "allocated cluster in error "
+                                               "code path.  Run chkdsk to "
+                                               "recover the lost cluster.");
+                               NVolSetErrors(vol);
+                       }
+                       up_write(&vol->lcnbmp_lock);
+               }
+       }
+       /*
+        * Resize the attribute record to its old size and rebuild the mapping
+        * pairs array.  Note, we only can do this if the runlist has been
+        * restored to its old state which also implies that the mapped
+        * attribute extent is not switched.
+        */
+       if (status.mp_rebuilt && !status.runlist_merged) {
+               if (ntfs_attr_record_resize(m, a, attr_rec_len)) {
+                       ntfs_error(vol->sb, "Failed to restore attribute "
+                                       "record in error code path.  Run "
+                                       "chkdsk to recover.");
+                       make_bad_inode(vi);
+                       make_bad_inode(VFS_I(base_ni));
+                       NVolSetErrors(vol);
+               } else /* if (success) */ {
+                       if (ntfs_mapping_pairs_build(vol, (u8*)a +
+                                       le16_to_cpu(a->data.non_resident.
+                                       mapping_pairs_offset), attr_rec_len -
+                                       le16_to_cpu(a->data.non_resident.
+                                       mapping_pairs_offset), ni->runlist.rl,
+                                       vcn, highest_vcn, NULL)) {
+                               ntfs_error(vol->sb, "Failed to restore "
+                                               "mapping pairs array in error "
+                                               "code path.  Run chkdsk to "
+                                               "recover.");
+                               make_bad_inode(vi);
+                               make_bad_inode(VFS_I(base_ni));
+                               NVolSetErrors(vol);
+                       }
+                       flush_dcache_mft_record_page(ctx->ntfs_ino);
+                       mark_mft_record_dirty(ctx->ntfs_ino);
+               }
+       }
+       /* Release the mft record and the attribute. */
+       if (status.mft_attr_mapped) {
+               ntfs_attr_put_search_ctx(ctx);
+               unmap_mft_record(base_ni);
+       }
+       /* Release the runlist lock. */
+       if (rl_write_locked)
+               up_write(&ni->runlist.lock);
+       else if (rl)
+               up_read(&ni->runlist.lock);
+       /*
+        * Zero out any newly allocated blocks to avoid exposing stale data.
+        * If BH_New is set, we know that the block was newly allocated above
+        * and that it has not been fully zeroed and marked dirty yet.
+        */
+       nr_pages = u;
+       u = 0;
+       end = bh_cpos << vol->cluster_size_bits;
+       do {
+               page = pages[u];
+               bh = head = page_buffers(page);
+               do {
+                       if (u == nr_pages &&
+                                       ((s64)page->index << PAGE_CACHE_SHIFT) +
+                                       bh_offset(bh) >= end)
+                               break;
+                       if (!buffer_new(bh))
+                               continue;
+                       clear_buffer_new(bh);
+                       if (!buffer_uptodate(bh)) {
+                               if (PageUptodate(page))
+                                       set_buffer_uptodate(bh);
+                               else {
+                                       u8 *kaddr = kmap_atomic(page, KM_USER0);
+                                       memset(kaddr + bh_offset(bh), 0,
+                                                       blocksize);
+                                       kunmap_atomic(kaddr, KM_USER0);
+                                       flush_dcache_page(page);
+                                       set_buffer_uptodate(bh);
+                               }
+                       }
+                       mark_buffer_dirty(bh);
+               } while ((bh = bh->b_this_page) != head);
+       } while (++u <= nr_pages);
+       ntfs_error(vol->sb, "Failed.  Returning error code %i.", err);
+       return err;
+}
+
+/*
+ * Copy as much as we can into the pages and return the number of bytes which
+ * were sucessfully copied.  If a fault is encountered then clear the pages
+ * out to (ofs + bytes) and return the number of bytes which were copied.
+ */
+static inline size_t ntfs_copy_from_user(struct page **pages,
+               unsigned nr_pages, unsigned ofs, const char __user *buf,
+               size_t bytes)
+{
+       struct page **last_page = pages + nr_pages;
+       char *kaddr;
+       size_t total = 0;
+       unsigned len;
+       int left;
+
+       do {
+               len = PAGE_CACHE_SIZE - ofs;
+               if (len > bytes)
+                       len = bytes;
+               kaddr = kmap_atomic(*pages, KM_USER0);
+               left = __copy_from_user_inatomic(kaddr + ofs, buf, len);
+               kunmap_atomic(kaddr, KM_USER0);
+               if (unlikely(left)) {
+                       /* Do it the slow way. */
+                       kaddr = kmap(*pages);
+                       left = __copy_from_user(kaddr + ofs, buf, len);
+                       kunmap(*pages);
+                       if (unlikely(left))
+                               goto err_out;
+               }
+               total += len;
+               bytes -= len;
+               if (!bytes)
+                       break;
+               buf += len;
+               ofs = 0;
+       } while (++pages < last_page);
+out:
+       return total;
+err_out:
+       total += len - left;
+       /* Zero the rest of the target like __copy_from_user(). */
+       while (++pages < last_page) {
+               bytes -= len;
+               if (!bytes)
+                       break;
+               len = PAGE_CACHE_SIZE;
+               if (len > bytes)
+                       len = bytes;
+               kaddr = kmap_atomic(*pages, KM_USER0);
+               memset(kaddr, 0, len);
+               kunmap_atomic(kaddr, KM_USER0);
+       }
+       goto out;
+}
+
+static size_t __ntfs_copy_from_user_iovec(char *vaddr,
+               const struct iovec *iov, size_t iov_ofs, size_t bytes)
+{
+       size_t total = 0;
+
+       while (1) {
+               const char __user *buf = iov->iov_base + iov_ofs;
+               unsigned len;
+               size_t left;
+
+               len = iov->iov_len - iov_ofs;
+               if (len > bytes)
+                       len = bytes;
+               left = __copy_from_user_inatomic(vaddr, buf, len);
+               total += len;
+               bytes -= len;
+               vaddr += len;
+               if (unlikely(left)) {
+                       /*
+                        * Zero the rest of the target like __copy_from_user().
+                        */
+                       memset(vaddr, 0, bytes);
+                       total -= left;
+                       break;
+               }
+               if (!bytes)
+                       break;
+               iov++;
+               iov_ofs = 0;
+       }
+       return total;
+}
+
+static inline void ntfs_set_next_iovec(const struct iovec **iovp,
+               size_t *iov_ofsp, size_t bytes)
+{
+       const struct iovec *iov = *iovp;
+       size_t iov_ofs = *iov_ofsp;
+
+       while (bytes) {
+               unsigned len;
+
+               len = iov->iov_len - iov_ofs;
+               if (len > bytes)
+                       len = bytes;
+               bytes -= len;
+               iov_ofs += len;
+               if (iov->iov_len == iov_ofs) {
+                       iov++;
+                       iov_ofs = 0;
+               }
+       }
+       *iovp = iov;
+       *iov_ofsp = iov_ofs;
+}
+
+/*
+ * This has the same side-effects and return value as ntfs_copy_from_user().
+ * The difference is that on a fault we need to memset the remainder of the
+ * pages (out to offset + bytes), to emulate ntfs_copy_from_user()'s
+ * single-segment behaviour.
+ *
+ * We call the same helper (__ntfs_copy_from_user_iovec()) both when atomic and
+ * when not atomic.  This is ok because __ntfs_copy_from_user_iovec() calls
+ * __copy_from_user_inatomic() and it is ok to call this when non-atomic.  In
+ * fact, the only difference between __copy_from_user_inatomic() and
+ * __copy_from_user() is that the latter calls might_sleep().  And on many
+ * architectures __copy_from_user_inatomic() is just defined to
+ * __copy_from_user() so it makes no difference at all on those architectures.
+ */
+static inline size_t ntfs_copy_from_user_iovec(struct page **pages,
+               unsigned nr_pages, unsigned ofs, const struct iovec **iov,
+               size_t *iov_ofs, size_t bytes)
+{
+       struct page **last_page = pages + nr_pages;
+       char *kaddr;
+       size_t copied, len, total = 0;
+
+       do {
+               len = PAGE_CACHE_SIZE - ofs;
+               if (len > bytes)
+                       len = bytes;
+               kaddr = kmap_atomic(*pages, KM_USER0);
+               copied = __ntfs_copy_from_user_iovec(kaddr + ofs,
+                               *iov, *iov_ofs, len);
+               kunmap_atomic(kaddr, KM_USER0);
+               if (unlikely(copied != len)) {
+                       /* Do it the slow way. */
+                       kaddr = kmap(*pages);
+                       copied = __ntfs_copy_from_user_iovec(kaddr + ofs,
+                                       *iov, *iov_ofs, len);
+                       kunmap(*pages);
+                       if (unlikely(copied != len))
+                               goto err_out;
+               }
+               total += len;
+               bytes -= len;
+               if (!bytes)
+                       break;
+               ntfs_set_next_iovec(iov, iov_ofs, len);
+               ofs = 0;
+       } while (++pages < last_page);
+out:
+       return total;
+err_out:
+       total += copied;
+       /* Zero the rest of the target like __copy_from_user(). */
+       while (++pages < last_page) {
+               bytes -= len;
+               if (!bytes)
+                       break;
+               len = PAGE_CACHE_SIZE;
+               if (len > bytes)
+                       len = bytes;
+               kaddr = kmap_atomic(*pages, KM_USER0);
+               memset(kaddr, 0, len);
+               kunmap_atomic(kaddr, KM_USER0);
+       }
+       goto out;
+}
+
+static inline void ntfs_flush_dcache_pages(struct page **pages,
+               unsigned nr_pages)
+{
+       BUG_ON(!nr_pages);
+       do {
+               /*
+                * Warning: Do not do the decrement at the same time as the
+                * call because flush_dcache_page() is a NULL macro on i386
+                * and hence the decrement never happens.
+                */
+               flush_dcache_page(pages[nr_pages]);
+       } while (--nr_pages > 0);
+}
+
+/**
+ * ntfs_commit_pages_after_non_resident_write - commit the received data
+ * @pages:     array of destination pages
+ * @nr_pages:  number of pages in @pages
+ * @pos:       byte position in file at which the write begins
+ * @bytes:     number of bytes to be written
+ *
+ * See description of ntfs_commit_pages_after_write(), below.
+ */
+static inline int ntfs_commit_pages_after_non_resident_write(
+               struct page **pages, const unsigned nr_pages,
+               s64 pos, size_t bytes)
+{
+       s64 end, initialized_size;
+       struct inode *vi;
+       ntfs_inode *ni, *base_ni;
+       struct buffer_head *bh, *head;
+       ntfs_attr_search_ctx *ctx;
+       MFT_RECORD *m;
+       ATTR_RECORD *a;
+       unsigned long flags;
+       unsigned blocksize, u;
+       int err;
+
+       vi = pages[0]->mapping->host;
+       ni = NTFS_I(vi);
+       blocksize = 1 << vi->i_blkbits;
+       end = pos + bytes;
+       u = 0;
+       do {
+               s64 bh_pos;
+               struct page *page;
+               BOOL partial;
+
+               page = pages[u];
+               bh_pos = (s64)page->index << PAGE_CACHE_SHIFT;
+               bh = head = page_buffers(page);
+               partial = FALSE;
+               do {
+                       s64 bh_end;
+
+                       bh_end = bh_pos + blocksize;
+                       if (bh_end <= pos || bh_pos >= end) {
+                               if (!buffer_uptodate(bh))
+                                       partial = TRUE;
+                       } else {
+                               set_buffer_uptodate(bh);
+                               mark_buffer_dirty(bh);
+                       }
+               } while (bh_pos += blocksize, (bh = bh->b_this_page) != head);
+               /*
+                * If all buffers are now uptodate but the page is not, set the
+                * page uptodate.
+                */
+               if (!partial && !PageUptodate(page))
+                       SetPageUptodate(page);
+       } while (++u < nr_pages);
+       /*
+        * Finally, if we do not need to update initialized_size or i_size we
+        * are finished.
+        */
+       read_lock_irqsave(&ni->size_lock, flags);
+       initialized_size = ni->initialized_size;
+       read_unlock_irqrestore(&ni->size_lock, flags);
+       if (end <= initialized_size) {
+               ntfs_debug("Done.");
+               return 0;
+       }
+       /*
+        * Update initialized_size/i_size as appropriate, both in the inode and
+        * the mft record.
+        */
+       if (!NInoAttr(ni))
+               base_ni = ni;
+       else
+               base_ni = ni->ext.base_ntfs_ino;
+       /* Map, pin, and lock the mft record. */
+       m = map_mft_record(base_ni);
+       if (IS_ERR(m)) {
+               err = PTR_ERR(m);
+               m = NULL;
+               ctx = NULL;
+               goto err_out;
+       }
+       BUG_ON(!NInoNonResident(ni));
+       ctx = ntfs_attr_get_search_ctx(base_ni, m);
+       if (unlikely(!ctx)) {
+               err = -ENOMEM;
+               goto err_out;
+       }
+       err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
+                       CASE_SENSITIVE, 0, NULL, 0, ctx);
+       if (unlikely(err)) {
+               if (err == -ENOENT)
+                       err = -EIO;
+               goto err_out;
+       }
+       a = ctx->attr;
+       BUG_ON(!a->non_resident);
+       write_lock_irqsave(&ni->size_lock, flags);
+       BUG_ON(end > ni->allocated_size);
+       ni->initialized_size = end;
+       a->data.non_resident.initialized_size = cpu_to_sle64(end);
+       if (end > i_size_read(vi)) {
+               i_size_write(vi, end);
+               a->data.non_resident.data_size =
+                               a->data.non_resident.initialized_size;
+       }
+       write_unlock_irqrestore(&ni->size_lock, flags);
+       /* Mark the mft record dirty, so it gets written back. */
+       flush_dcache_mft_record_page(ctx->ntfs_ino);
+       mark_mft_record_dirty(ctx->ntfs_ino);
+       ntfs_attr_put_search_ctx(ctx);
+       unmap_mft_record(base_ni);
+       ntfs_debug("Done.");
+       return 0;
+err_out:
+       if (ctx)
+               ntfs_attr_put_search_ctx(ctx);
+       if (m)
+               unmap_mft_record(base_ni);
+       ntfs_error(vi->i_sb, "Failed to update initialized_size/i_size (error "
+                       "code %i).", err);
+       if (err != -ENOMEM) {
+               NVolSetErrors(ni->vol);
+               make_bad_inode(VFS_I(base_ni));
+               make_bad_inode(vi);
+       }
+       return err;
+}
+
+/**
+ * ntfs_commit_pages_after_write - commit the received data
+ * @pages:     array of destination pages
+ * @nr_pages:  number of pages in @pages
+ * @pos:       byte position in file at which the write begins
+ * @bytes:     number of bytes to be written
+ *
+ * This is called from ntfs_file_buffered_write() with i_sem held on the inode
+ * (@pages[0]->mapping->host).  There are @nr_pages pages in @pages which are
+ * locked but not kmap()ped.  The source data has already been copied into the
+ * @page.  ntfs_prepare_pages_for_non_resident_write() has been called before
+ * the data was copied (for non-resident attributes only) and it returned
+ * success.
+ *
+ * Need to set uptodate and mark dirty all buffers within the boundary of the
+ * write.  If all buffers in a page are uptodate we set the page uptodate, too.
+ *
+ * Setting the buffers dirty ensures that they get written out later when
+ * ntfs_writepage() is invoked by the VM.
+ *
+ * Finally, we need to update i_size and initialized_size as appropriate both
+ * in the inode and the mft record.
+ *
+ * This is modelled after fs/buffer.c::generic_commit_write(), which marks
+ * buffers uptodate and dirty, sets the page uptodate if all buffers in the
+ * page are uptodate, and updates i_size if the end of io is beyond i_size.  In
+ * that case, it also marks the inode dirty.
+ *
+ * If things have gone as outlined in
+ * ntfs_prepare_pages_for_non_resident_write(), we do not need to do any page
+ * content modifications here for non-resident attributes.  For resident
+ * attributes we need to do the uptodate bringing here which we combine with
+ * the copying into the mft record which means we save one atomic kmap.
+ *
+ * Return 0 on success or -errno on error.
+ */
+static int ntfs_commit_pages_after_write(struct page **pages,
+               const unsigned nr_pages, s64 pos, size_t bytes)
+{
+       s64 end, initialized_size;
+       loff_t i_size;
+       struct inode *vi;
+       ntfs_inode *ni, *base_ni;
+       struct page *page;
+       ntfs_attr_search_ctx *ctx;
+       MFT_RECORD *m;
+       ATTR_RECORD *a;
+       char *kattr, *kaddr;
+       unsigned long flags;
+       u32 attr_len;
+       int err;
+
+       BUG_ON(!nr_pages);
+       BUG_ON(!pages);
+       page = pages[0];
+       BUG_ON(!page);
+       vi = page->mapping->host;
+       ni = NTFS_I(vi);
+       ntfs_debug("Entering for inode 0x%lx, attribute type 0x%x, start page "
+                       "index 0x%lx, nr_pages 0x%x, pos 0x%llx, bytes 0x%zx.",
+                       vi->i_ino, ni->type, page->index, nr_pages,
+                       (long long)pos, bytes);
+       if (NInoNonResident(ni))
+               return ntfs_commit_pages_after_non_resident_write(pages,
+                               nr_pages, pos, bytes);
+       BUG_ON(nr_pages > 1);
+       /*
+        * Attribute is resident, implying it is not compressed, encrypted, or
+        * sparse.
+        */
+       if (!NInoAttr(ni))
+               base_ni = ni;
+       else
+               base_ni = ni->ext.base_ntfs_ino;
+       BUG_ON(NInoNonResident(ni));
+       /* Map, pin, and lock the mft record. */
+       m = map_mft_record(base_ni);
+       if (IS_ERR(m)) {
+               err = PTR_ERR(m);
+               m = NULL;
+               ctx = NULL;
+               goto err_out;
+       }
+       ctx = ntfs_attr_get_search_ctx(base_ni, m);
+       if (unlikely(!ctx)) {
+               err = -ENOMEM;
+               goto err_out;
+       }
+       err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
+                       CASE_SENSITIVE, 0, NULL, 0, ctx);
+       if (unlikely(err)) {
+               if (err == -ENOENT)
+                       err = -EIO;
+               goto err_out;
+       }
+       a = ctx->attr;
+       BUG_ON(a->non_resident);
+       /* The total length of the attribute value. */
+       attr_len = le32_to_cpu(a->data.resident.value_length);
+       i_size = i_size_read(vi);
+       BUG_ON(attr_len != i_size);
+       BUG_ON(pos > attr_len);
+       end = pos + bytes;
+       BUG_ON(end > le32_to_cpu(a->length) -
+                       le16_to_cpu(a->data.resident.value_offset));
+       kattr = (u8*)a + le16_to_cpu(a->data.resident.value_offset);
+       kaddr = kmap_atomic(page, KM_USER0);
+       /* Copy the received data from the page to the mft record. */
+       memcpy(kattr + pos, kaddr + pos, bytes);
+       /* Update the attribute length if necessary. */
+       if (end > attr_len) {
+               attr_len = end;
+               a->data.resident.value_length = cpu_to_le32(attr_len);
+       }
+       /*
+        * If the page is not uptodate, bring the out of bounds area(s)
+        * uptodate by copying data from the mft record to the page.
+        */
+       if (!PageUptodate(page)) {
+               if (pos > 0)
+                       memcpy(kaddr, kattr, pos);
+               if (end < attr_len)
+                       memcpy(kaddr + end, kattr + end, attr_len - end);
+               /* Zero the region outside the end of the attribute value. */
+               memset(kaddr + attr_len, 0, PAGE_CACHE_SIZE - attr_len);
+               flush_dcache_page(page);
+               SetPageUptodate(page);
+       }
+       kunmap_atomic(kaddr, KM_USER0);
+       /* Update initialized_size/i_size if necessary. */
+       read_lock_irqsave(&ni->size_lock, flags);
+       initialized_size = ni->initialized_size;
+       BUG_ON(end > ni->allocated_size);
+       read_unlock_irqrestore(&ni->size_lock, flags);
+       BUG_ON(initialized_size != i_size);
+       if (end > initialized_size) {
+               unsigned long flags;
+
+               write_lock_irqsave(&ni->size_lock, flags);
+               ni->initialized_size = end;
+               i_size_write(vi, end);
+               write_unlock_irqrestore(&ni->size_lock, flags);
+       }
+       /* Mark the mft record dirty, so it gets written back. */
+       flush_dcache_mft_record_page(ctx->ntfs_ino);
+       mark_mft_record_dirty(ctx->ntfs_ino);
+       ntfs_attr_put_search_ctx(ctx);
+       unmap_mft_record(base_ni);
+       ntfs_debug("Done.");
+       return 0;
+err_out:
+       if (err == -ENOMEM) {
+               ntfs_warning(vi->i_sb, "Error allocating memory required to "
+                               "commit the write.");
+               if (PageUptodate(page)) {
+                       ntfs_warning(vi->i_sb, "Page is uptodate, setting "
+                                       "dirty so the write will be retried "
+                                       "later on by the VM.");
+                       /*
+                        * Put the page on mapping->dirty_pages, but leave its
+                        * buffers' dirty state as-is.
+                        */
+                       __set_page_dirty_nobuffers(page);
+                       err = 0;
+               } else
+                       ntfs_error(vi->i_sb, "Page is not uptodate.  Written "
+                                       "data has been lost.");
+       } else {
+               ntfs_error(vi->i_sb, "Resident attribute commit write failed "
+                               "with error %i.", err);
+               NVolSetErrors(ni->vol);
+               make_bad_inode(VFS_I(base_ni));
+               make_bad_inode(vi);
+       }
+       if (ctx)
+               ntfs_attr_put_search_ctx(ctx);
+       if (m)
+               unmap_mft_record(base_ni);
+       return err;
+}
+
+/**
+ * ntfs_file_buffered_write -
+ *
+ * Locking: The vfs is holding ->i_sem on the inode.
+ */
+static ssize_t ntfs_file_buffered_write(struct kiocb *iocb,
+               const struct iovec *iov, unsigned long nr_segs,
+               loff_t pos, loff_t *ppos, size_t count)
+{
+       struct file *file = iocb->ki_filp;
+       struct address_space *mapping = file->f_mapping;
+       struct inode *vi = mapping->host;
+       ntfs_inode *ni = NTFS_I(vi);
+       ntfs_volume *vol = ni->vol;
+       struct page *pages[NTFS_MAX_PAGES_PER_CLUSTER];
+       struct page *cached_page = NULL;
+       char __user *buf = NULL;
+       s64 end, ll;
+       VCN last_vcn;
+       LCN lcn;
+       unsigned long flags;
+       size_t bytes, iov_ofs = 0;      /* Offset in the current iovec. */
+       ssize_t status, written;
+       unsigned nr_pages;
+       int err;
+       struct pagevec lru_pvec;
+
+       ntfs_debug("Entering for i_ino 0x%lx, attribute type 0x%x, "
+                       "pos 0x%llx, count 0x%lx.",
+                       vi->i_ino, (unsigned)le32_to_cpu(ni->type),
+                       (unsigned long long)pos, (unsigned long)count);
+       if (unlikely(!count))
+               return 0;
+       BUG_ON(NInoMstProtected(ni));
+       /*
+        * If the attribute is not an index root and it is encrypted or
+        * compressed, we cannot write to it yet.  Note we need to check for
+        * AT_INDEX_ALLOCATION since this is the type of both directory and
+        * index inodes.
+        */
+       if (ni->type != AT_INDEX_ALLOCATION) {
+               /* If file is encrypted, deny access, just like NT4. */
+               if (NInoEncrypted(ni)) {
+                       /*
+                        * Reminder for later: Encrypted files are _always_
+                        * non-resident so that the content can always be
+                        * encrypted.
+                        */
+                       ntfs_debug("Denying write access to encrypted file.");
+                       return -EACCES;
+               }
+               if (NInoCompressed(ni)) {
+                       /* Only unnamed $DATA attribute can be compressed. */
+                       BUG_ON(ni->type != AT_DATA);
+                       BUG_ON(ni->name_len);
+                       /*
+                        * Reminder for later: If resident, the data is not
+                        * actually compressed.  Only on the switch to non-
+                        * resident does compression kick in.  This is in
+                        * contrast to encrypted files (see above).
+                        */
+                       ntfs_error(vi->i_sb, "Writing to compressed files is "
+                                       "not implemented yet.  Sorry.");
+                       return -EOPNOTSUPP;
+               }
+       }
+       /*
+        * If a previous ntfs_truncate() failed, repeat it and abort if it
+        * fails again.
+        */
+       if (unlikely(NInoTruncateFailed(ni))) {
+               down_write(&vi->i_alloc_sem);
+               err = ntfs_truncate(vi);
+               up_write(&vi->i_alloc_sem);
+               if (err || NInoTruncateFailed(ni)) {
+                       if (!err)
+                               err = -EIO;
+                       ntfs_error(vol->sb, "Cannot perform write to inode "
+                                       "0x%lx, attribute type 0x%x, because "
+                                       "ntfs_truncate() failed (error code "
+                                       "%i).", vi->i_ino,
+                                       (unsigned)le32_to_cpu(ni->type), err);
+                       return err;
+               }
+       }
+       /* The first byte after the write. */
+       end = pos + count;
+       /*
+        * If the write goes beyond the allocated size, extend the allocation
+        * to cover the whole of the write, rounded up to the nearest cluster.
+        */
+       read_lock_irqsave(&ni->size_lock, flags);
+       ll = ni->allocated_size;
+       read_unlock_irqrestore(&ni->size_lock, flags);
+       if (end > ll) {
+               /* Extend the allocation without changing the data size. */
+               ll = ntfs_attr_extend_allocation(ni, end, -1, pos);
+               if (likely(ll >= 0)) {
+                       BUG_ON(pos >= ll);
+                       /* If the extension was partial truncate the write. */
+                       if (end > ll) {
+                               ntfs_debug("Truncating write to inode 0x%lx, "
+                                               "attribute type 0x%x, because "
+                                               "the allocation was only "
+                                               "partially extended.",
+                                               vi->i_ino, (unsigned)
+                                               le32_to_cpu(ni->type));
+                               end = ll;
+                               count = ll - pos;
+                       }
+               } else {
+                       err = ll;
+                       read_lock_irqsave(&ni->size_lock, flags);
+                       ll = ni->allocated_size;
+                       read_unlock_irqrestore(&ni->size_lock, flags);
+                       /* Perform a partial write if possible or fail. */
+                       if (pos < ll) {
+                               ntfs_debug("Truncating write to inode 0x%lx, "
+                                               "attribute type 0x%x, because "
+                                               "extending the allocation "
+                                               "failed (error code %i).",
+                                               vi->i_ino, (unsigned)
+                                               le32_to_cpu(ni->type), err);
+                               end = ll;
+                               count = ll - pos;
+                       } else {
+                               ntfs_error(vol->sb, "Cannot perform write to "
+                                               "inode 0x%lx, attribute type "
+                                               "0x%x, because extending the "
+                                               "allocation failed (error "
+                                               "code %i).", vi->i_ino,
+                                               (unsigned)
+                                               le32_to_cpu(ni->type), err);
+                               return err;
+                       }
+               }
+       }
+       pagevec_init(&lru_pvec, 0);
+       written = 0;
+       /*
+        * If the write starts beyond the initialized size, extend it up to the
+        * beginning of the write and initialize all non-sparse space between
+        * the old initialized size and the new one.  This automatically also
+        * increments the vfs inode->i_size to keep it above or equal to the
+        * initialized_size.
+        */
+       read_lock_irqsave(&ni->size_lock, flags);
+       ll = ni->initialized_size;
+       read_unlock_irqrestore(&ni->size_lock, flags);
+       if (pos > ll) {
+               err = ntfs_attr_extend_initialized(ni, pos, &cached_page,
+                               &lru_pvec);
+               if (err < 0) {
+                       ntfs_error(vol->sb, "Cannot perform write to inode "
+                                       "0x%lx, attribute type 0x%x, because "
+                                       "extending the initialized size "
+                                       "failed (error code %i).", vi->i_ino,
+                                       (unsigned)le32_to_cpu(ni->type), err);
+                       status = err;
+                       goto err_out;
+               }
+       }
+       /*
+        * Determine the number of pages per cluster for non-resident
+        * attributes.
+        */
+       nr_pages = 1;
+       if (vol->cluster_size > PAGE_CACHE_SIZE && NInoNonResident(ni))
+               nr_pages = vol->cluster_size >> PAGE_CACHE_SHIFT;
+       /* Finally, perform the actual write. */
+       last_vcn = -1;
+       if (likely(nr_segs == 1))
+               buf = iov->iov_base;
+       do {
+               VCN vcn;
+               pgoff_t idx, start_idx;
+               unsigned ofs, do_pages, u;
+               size_t copied;
+
+               start_idx = idx = pos >> PAGE_CACHE_SHIFT;
+               ofs = pos & ~PAGE_CACHE_MASK;
+               bytes = PAGE_CACHE_SIZE - ofs;
+               do_pages = 1;
+               if (nr_pages > 1) {
+                       vcn = pos >> vol->cluster_size_bits;
+                       if (vcn != last_vcn) {
+                               last_vcn = vcn;
+                               /*
+                                * Get the lcn of the vcn the write is in.  If
+                                * it is a hole, need to lock down all pages in
+                                * the cluster.
+                                */
+                               down_read(&ni->runlist.lock);
+                               lcn = ntfs_attr_vcn_to_lcn_nolock(ni, pos >>
+                                               vol->cluster_size_bits, FALSE);
+                               up_read(&ni->runlist.lock);
+                               if (unlikely(lcn < LCN_HOLE)) {
+                                       status = -EIO;
+                                       if (lcn == LCN_ENOMEM)
+                                               status = -ENOMEM;
+                                       else
+                                               ntfs_error(vol->sb, "Cannot "
+                                                       "perform write to "
+                                                       "inode 0x%lx, "
+                                                       "attribute type 0x%x, "
+                                                       "because the attribute "
+                                                       "is corrupt.",
+                                                       vi->i_ino, (unsigned)
+                                                       le32_to_cpu(ni->type));
+                                       break;
+                               }
+                               if (lcn == LCN_HOLE) {
+                                       start_idx = (pos & ~(s64)
+                                                       vol->cluster_size_mask)
+                                                       >> PAGE_CACHE_SHIFT;
+                                       bytes = vol->cluster_size - (pos &
+                                                       vol->cluster_size_mask);
+                                       do_pages = nr_pages;
+                               }
+                       }
+               }
+               if (bytes > count)
+                       bytes = count;
+               /*
+                * Bring in the user page(s) that we will copy from _first_.
+                * Otherwise there is a nasty deadlock on copying from the same
+                * page(s) as we are writing to, without it/them being marked
+                * up-to-date.  Note, at present there is nothing to stop the
+                * pages being swapped out between us bringing them into memory
+                * and doing the actual copying.
+                */
+               if (likely(nr_segs == 1))
+                       ntfs_fault_in_pages_readable(buf, bytes);
+               else
+                       ntfs_fault_in_pages_readable_iovec(iov, iov_ofs, bytes);
+               /* Get and lock @do_pages starting at index @start_idx. */
+               status = __ntfs_grab_cache_pages(mapping, start_idx, do_pages,
+                               pages, &cached_page, &lru_pvec);
+               if (unlikely(status))
+                       break;
+               /*
+                * For non-resident attributes, we need to fill any holes with
+                * actual clusters and ensure all bufferes are mapped.  We also
+                * need to bring uptodate any buffers that are only partially
+                * being written to.
+                */
+               if (NInoNonResident(ni)) {
+                       status = ntfs_prepare_pages_for_non_resident_write(
+                                       pages, do_pages, pos, bytes);
+                       if (unlikely(status)) {
+                               loff_t i_size;
+
+                               do {
+                                       unlock_page(pages[--do_pages]);
+                                       page_cache_release(pages[do_pages]);
+                               } while (do_pages);
+                               /*
+                                * The write preparation may have instantiated
+                                * allocated space outside i_size.  Trim this
+                                * off again.  We can ignore any errors in this
+                                * case as we will just be waisting a bit of
+                                * allocated space, which is not a disaster.
+                                */
+                               i_size = i_size_read(vi);
+                               if (pos + bytes > i_size)
+                                       vmtruncate(vi, i_size);
+                               break;
+                       }
+               }
+               u = (pos >> PAGE_CACHE_SHIFT) - pages[0]->index;
+               if (likely(nr_segs == 1)) {
+                       copied = ntfs_copy_from_user(pages + u, do_pages - u,
+                                       ofs, buf, bytes);
+                       buf += copied;
+               } else
+                       copied = ntfs_copy_from_user_iovec(pages + u,
+                                       do_pages - u, ofs, &iov, &iov_ofs,
+                                       bytes);
+               ntfs_flush_dcache_pages(pages + u, do_pages - u);
+               status = ntfs_commit_pages_after_write(pages, do_pages, pos,
+                               bytes);
+               if (likely(!status)) {
+                       written += copied;
+                       count -= copied;
+                       pos += copied;
+                       if (unlikely(copied != bytes))
+                               status = -EFAULT;
+               }
+               do {
+                       unlock_page(pages[--do_pages]);
+                       mark_page_accessed(pages[do_pages]);
+                       page_cache_release(pages[do_pages]);
+               } while (do_pages);
+               if (unlikely(status))
+                       break;
+               balance_dirty_pages_ratelimited(mapping);
+               cond_resched();
+       } while (count);
+err_out:
+       *ppos = pos;
+       if (cached_page)
+               page_cache_release(cached_page);
+       /* For now, when the user asks for O_SYNC, we actually give O_DSYNC. */
+       if (likely(!status)) {
+               if (unlikely((file->f_flags & O_SYNC) || IS_SYNC(vi))) {
+                       if (!mapping->a_ops->writepage || !is_sync_kiocb(iocb))
+                               status = generic_osync_inode(vi, mapping,
+                                               OSYNC_METADATA|OSYNC_DATA);
+               }
+       }
+       pagevec_lru_add(&lru_pvec);
+       ntfs_debug("Done.  Returning %s (written 0x%lx, status %li).",
+                       written ? "written" : "status", (unsigned long)written,
+                       (long)status);
+       return written ? written : status;
+}
+
+/**
+ * ntfs_file_aio_write_nolock -
+ */
+static ssize_t ntfs_file_aio_write_nolock(struct kiocb *iocb,
+               const struct iovec *iov, unsigned long nr_segs, loff_t *ppos)
+{
+       struct file *file = iocb->ki_filp;
+       struct address_space *mapping = file->f_mapping;
+       struct inode *inode = mapping->host;
+       loff_t pos;
+       unsigned long seg;
+       size_t count;           /* after file limit checks */
+       ssize_t written, err;
+
+       count = 0;
+       for (seg = 0; seg < nr_segs; seg++) {
+               const struct iovec *iv = &iov[seg];
+               /*
+                * If any segment has a negative length, or the cumulative
+                * length ever wraps negative then return -EINVAL.
+                */
+               count += iv->iov_len;
+               if (unlikely((ssize_t)(count|iv->iov_len) < 0))
+                       return -EINVAL;
+               if (access_ok(VERIFY_READ, iv->iov_base, iv->iov_len))
+                       continue;
+               if (!seg)
+                       return -EFAULT;
+               nr_segs = seg;
+               count -= iv->iov_len;   /* This segment is no good */
+               break;
+       }
+       pos = *ppos;
+       vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
+       /* We can write back this queue in page reclaim. */
+       current->backing_dev_info = mapping->backing_dev_info;
+       written = 0;
+       err = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode));
+       if (err)
+               goto out;
+       if (!count)
+               goto out;
+       err = remove_suid(file->f_dentry);
+       if (err)
+               goto out;
+       inode_update_time(inode, 1);
+       written = ntfs_file_buffered_write(iocb, iov, nr_segs, pos, ppos,
+                       count);
+out:
+       current->backing_dev_info = NULL;
+       return written ? written : err;
+}
+
+/**
+ * ntfs_file_aio_write -
+ */
+static ssize_t ntfs_file_aio_write(struct kiocb *iocb, const char __user *buf,
+               size_t count, loff_t pos)
+{
+       struct file *file = iocb->ki_filp;
+       struct address_space *mapping = file->f_mapping;
+       struct inode *inode = mapping->host;
+       ssize_t ret;
+       struct iovec local_iov = { .iov_base = (void __user *)buf,
+                                  .iov_len = count };
+
+       BUG_ON(iocb->ki_pos != pos);
+
+       down(&inode->i_sem);
+       ret = ntfs_file_aio_write_nolock(iocb, &local_iov, 1, &iocb->ki_pos);
+       up(&inode->i_sem);
+       if (ret > 0 && ((file->f_flags & O_SYNC) || IS_SYNC(inode))) {
+               int err = sync_page_range(inode, mapping, pos, ret);
+               if (err < 0)
+                       ret = err;
+       }
+       return ret;
+}
+
+/**
+ * ntfs_file_writev -
+ *
+ * Basically the same as generic_file_writev() except that it ends up calling
+ * ntfs_file_aio_write_nolock() instead of __generic_file_aio_write_nolock().
+ */
+static ssize_t ntfs_file_writev(struct file *file, const struct iovec *iov,
+               unsigned long nr_segs, loff_t *ppos)
+{
+       struct address_space *mapping = file->f_mapping;
+       struct inode *inode = mapping->host;
+       struct kiocb kiocb;
+       ssize_t ret;
+
+       down(&inode->i_sem);
+       init_sync_kiocb(&kiocb, file);
+       ret = ntfs_file_aio_write_nolock(&kiocb, iov, nr_segs, ppos);
+       if (ret == -EIOCBQUEUED)
+               ret = wait_on_sync_kiocb(&kiocb);
+       up(&inode->i_sem);
+       if (ret > 0 && ((file->f_flags & O_SYNC) || IS_SYNC(inode))) {
+               int err = sync_page_range(inode, mapping, *ppos - ret, ret);
+               if (err < 0)
+                       ret = err;
+       }
+       return ret;
+}
+
+/**
+ * ntfs_file_write - simple wrapper for ntfs_file_writev()
+ */
+static ssize_t ntfs_file_write(struct file *file, const char __user *buf,
+               size_t count, loff_t *ppos)
+{
+       struct iovec local_iov = { .iov_base = (void __user *)buf,
+                                  .iov_len = count };
+
+       return ntfs_file_writev(file, &local_iov, 1, ppos);
+}
+
 /**
  * ntfs_file_fsync - sync a file to disk
  * @filp:      file to be synced
@@ -113,39 +2305,39 @@ static int ntfs_file_fsync(struct file *filp, struct dentry *dentry,
 #endif /* NTFS_RW */
 
 struct file_operations ntfs_file_ops = {
-       .llseek         = generic_file_llseek,    /* Seek inside file. */
-       .read           = generic_file_read,      /* Read from file. */
-       .aio_read       = generic_file_aio_read,  /* Async read from file. */
-       .readv          = generic_file_readv,     /* Read from file. */
+       .llseek         = generic_file_llseek,   /* Seek inside file. */
+       .read           = generic_file_read,     /* Read from file. */
+       .aio_read       = generic_file_aio_read, /* Async read from file. */
+       .readv          = generic_file_readv,    /* Read from file. */
 #ifdef NTFS_RW
-       .write          = generic_file_write,     /* Write to file. */
-       .aio_write      = generic_file_aio_write, /* Async write to file. */
-       .writev         = generic_file_writev,    /* Write to file. */
-       /*.release      = ,*/                     /* Last file is closed.  See
-                                                    fs/ext2/file.c::
-                                                    ext2_release_file() for
-                                                    how to use this to discard
-                                                    preallocated space for
-                                                    write opened files. */
-       .fsync          = ntfs_file_fsync,        /* Sync a file to disk. */
-       /*.aio_fsync    = ,*/                     /* Sync all outstanding async
-                                                    i/o operations on a
-                                                    kiocb. */
+       .write          = ntfs_file_write,       /* Write to file. */
+       .aio_write      = ntfs_file_aio_write,   /* Async write to file. */
+       .writev         = ntfs_file_writev,      /* Write to file. */
+       /*.release      = ,*/                    /* Last file is closed.  See
+                                                   fs/ext2/file.c::
+                                                   ext2_release_file() for
+                                                   how to use this to discard
+                                                   preallocated space for
+                                                   write opened files. */
+       .fsync          = ntfs_file_fsync,       /* Sync a file to disk. */
+       /*.aio_fsync    = ,*/                    /* Sync all outstanding async
+                                                   i/o operations on a
+                                                   kiocb. */
 #endif /* NTFS_RW */
-       /*.ioctl        = ,*/                     /* Perform function on the
-                                                    mounted filesystem. */
-       .mmap           = generic_file_mmap,      /* Mmap file. */
-       .open           = ntfs_file_open,         /* Open file. */
-       .sendfile       = generic_file_sendfile,  /* Zero-copy data send with
-                                                    the data source being on
-                                                    the ntfs partition.  We
-                                                    do not need to care about
-                                                    the data destination. */
-       /*.sendpage     = ,*/                     /* Zero-copy data send with
-                                                    the data destination being
-                                                    on the ntfs partition.  We
-                                                    do not need to care about
-                                                    the data source. */
+       /*.ioctl        = ,*/                    /* Perform function on the
+                                                   mounted filesystem. */
+       .mmap           = generic_file_mmap,     /* Mmap file. */
+       .open           = ntfs_file_open,        /* Open file. */
+       .sendfile       = generic_file_sendfile, /* Zero-copy data send with
+                                                   the data source being on
+                                                   the ntfs partition.  We do
+                                                   not need to care about the
+                                                   data destination. */
+       /*.sendpage     = ,*/                    /* Zero-copy data send with
+                                                   the data destination being
+                                                   on the ntfs partition.  We
+                                                   do not need to care about
+                                                   the data source. */
 };
 
 struct inode_operations ntfs_file_inode_ops = {
index 7ec045131808b14546333a16009746729a2d236d..b24f4c4b2c5ccff9e80a8018ceb56b296b7ae74c 100644 (file)
@@ -30,6 +30,7 @@
 #include "debug.h"
 #include "inode.h"
 #include "attrib.h"
+#include "lcnalloc.h"
 #include "malloc.h"
 #include "mft.h"
 #include "time.h"
@@ -2291,11 +2292,16 @@ int ntfs_show_options(struct seq_file *sf, struct vfsmount *mnt)
 
 #ifdef NTFS_RW
 
+static const char *es = "  Leaving inconsistent metadata.  Unmount and run "
+               "chkdsk.";
+
 /**
  * ntfs_truncate - called when the i_size of an ntfs inode is changed
  * @vi:                inode for which the i_size was changed
  *
- * We do not support i_size changes yet.
+ * We only support i_size changes for normal files at present, i.e. not
+ * compressed and not encrypted.  This is enforced in ntfs_setattr(), see
+ * below.
  *
  * The kernel guarantees that @vi is a regular file (S_ISREG() is true) and
  * that the change is allowed.
@@ -2306,80 +2312,499 @@ int ntfs_show_options(struct seq_file *sf, struct vfsmount *mnt)
  * Returns 0 on success or -errno on error.
  *
  * Called with ->i_sem held.  In all but one case ->i_alloc_sem is held for
- * writing.  The only case where ->i_alloc_sem is not held is
+ * writing.  The only case in the kernel where ->i_alloc_sem is not held is
  * mm/filemap.c::generic_file_buffered_write() where vmtruncate() is called
- * with the current i_size as the offset which means that it is a noop as far
- * as ntfs_truncate() is concerned.
+ * with the current i_size as the offset.  The analogous place in NTFS is in
+ * fs/ntfs/file.c::ntfs_file_buffered_write() where we call vmtruncate() again
+ * without holding ->i_alloc_sem.
  */
 int ntfs_truncate(struct inode *vi)
 {
-       ntfs_inode *ni = NTFS_I(vi);
+       s64 new_size, old_size, nr_freed, new_alloc_size, old_alloc_size;
+       VCN highest_vcn;
+       unsigned long flags;
+       ntfs_inode *base_ni, *ni = NTFS_I(vi);
        ntfs_volume *vol = ni->vol;
        ntfs_attr_search_ctx *ctx;
        MFT_RECORD *m;
        ATTR_RECORD *a;
        const char *te = "  Leaving file length out of sync with i_size.";
-       int err;
+       int err, mp_size, size_change, alloc_change;
+       u32 attr_len;
 
        ntfs_debug("Entering for inode 0x%lx.", vi->i_ino);
        BUG_ON(NInoAttr(ni));
+       BUG_ON(S_ISDIR(vi->i_mode));
+       BUG_ON(NInoMstProtected(ni));
        BUG_ON(ni->nr_extents < 0);
-       m = map_mft_record(ni);
+retry_truncate:
+       /*
+        * Lock the runlist for writing and map the mft record to ensure it is
+        * safe to mess with the attribute runlist and sizes.
+        */
+       down_write(&ni->runlist.lock);
+       if (!NInoAttr(ni))
+               base_ni = ni;
+       else
+               base_ni = ni->ext.base_ntfs_ino;
+       m = map_mft_record(base_ni);
        if (IS_ERR(m)) {
                err = PTR_ERR(m);
                ntfs_error(vi->i_sb, "Failed to map mft record for inode 0x%lx "
                                "(error code %d).%s", vi->i_ino, err, te);
                ctx = NULL;
                m = NULL;
-               goto err_out;
+               goto old_bad_out;
        }
-       ctx = ntfs_attr_get_search_ctx(ni, m);
+       ctx = ntfs_attr_get_search_ctx(base_ni, m);
        if (unlikely(!ctx)) {
                ntfs_error(vi->i_sb, "Failed to allocate a search context for "
                                "inode 0x%lx (not enough memory).%s",
                                vi->i_ino, te);
                err = -ENOMEM;
-               goto err_out;
+               goto old_bad_out;
        }
        err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
                        CASE_SENSITIVE, 0, NULL, 0, ctx);
        if (unlikely(err)) {
-               if (err == -ENOENT)
+               if (err == -ENOENT) {
                        ntfs_error(vi->i_sb, "Open attribute is missing from "
                                        "mft record.  Inode 0x%lx is corrupt.  "
-                                       "Run chkdsk.", vi->i_ino);
-               else
+                                       "Run chkdsk.%s", vi->i_ino, te);
+                       err = -EIO;
+               } else
                        ntfs_error(vi->i_sb, "Failed to lookup attribute in "
-                                       "inode 0x%lx (error code %d).",
-                                       vi->i_ino, err);
-               goto err_out;
+                                       "inode 0x%lx (error code %d).%s",
+                                       vi->i_ino, err, te);
+               goto old_bad_out;
        }
+       m = ctx->mrec;
        a = ctx->attr;
-       /* If the size has not changed there is nothing to do. */
-       if (ntfs_attr_size(a) == i_size_read(vi))
-               goto done;
-       // TODO: Implement the truncate...
-       ntfs_error(vi->i_sb, "Inode size has changed but this is not "
-                       "implemented yet.  Resetting inode size to old value. "
-                       " This is most likely a bug in the ntfs driver!");
-       i_size_write(vi, ntfs_attr_size(a)); 
-done:
+       /*
+        * The i_size of the vfs inode is the new size for the attribute value.
+        */
+       new_size = i_size_read(vi);
+       /* The current size of the attribute value is the old size. */
+       old_size = ntfs_attr_size(a);
+       /* Calculate the new allocated size. */
+       if (NInoNonResident(ni))
+               new_alloc_size = (new_size + vol->cluster_size - 1) &
+                               ~(s64)vol->cluster_size_mask;
+       else
+               new_alloc_size = (new_size + 7) & ~7;
+       /* The current allocated size is the old allocated size. */
+       read_lock_irqsave(&ni->size_lock, flags);
+       old_alloc_size = ni->allocated_size;
+       read_unlock_irqrestore(&ni->size_lock, flags);
+       /*
+        * The change in the file size.  This will be 0 if no change, >0 if the
+        * size is growing, and <0 if the size is shrinking.
+        */
+       size_change = -1;
+       if (new_size - old_size >= 0) {
+               size_change = 1;
+               if (new_size == old_size)
+                       size_change = 0;
+       }
+       /* As above for the allocated size. */
+       alloc_change = -1;
+       if (new_alloc_size - old_alloc_size >= 0) {
+               alloc_change = 1;
+               if (new_alloc_size == old_alloc_size)
+                       alloc_change = 0;
+       }
+       /*
+        * If neither the size nor the allocation are being changed there is
+        * nothing to do.
+        */
+       if (!size_change && !alloc_change)
+               goto unm_done;
+       /* If the size is changing, check if new size is allowed in $AttrDef. */
+       if (size_change) {
+               err = ntfs_attr_size_bounds_check(vol, ni->type, new_size);
+               if (unlikely(err)) {
+                       if (err == -ERANGE) {
+                               ntfs_error(vol->sb, "Truncate would cause the "
+                                               "inode 0x%lx to %simum size "
+                                               "for its attribute type "
+                                               "(0x%x).  Aborting truncate.",
+                                               vi->i_ino,
+                                               new_size > old_size ? "exceed "
+                                               "the max" : "go under the min",
+                                               le32_to_cpu(ni->type));
+                               err = -EFBIG;
+                       } else {
+                               ntfs_error(vol->sb, "Inode 0x%lx has unknown "
+                                               "attribute type 0x%x.  "
+                                               "Aborting truncate.",
+                                               vi->i_ino,
+                                               le32_to_cpu(ni->type));
+                               err = -EIO;
+                       }
+                       /* Reset the vfs inode size to the old size. */
+                       i_size_write(vi, old_size);
+                       goto err_out;
+               }
+       }
+       if (NInoCompressed(ni) || NInoEncrypted(ni)) {
+               ntfs_warning(vi->i_sb, "Changes in inode size are not "
+                               "supported yet for %s files, ignoring.",
+                               NInoCompressed(ni) ? "compressed" :
+                               "encrypted");
+               err = -EOPNOTSUPP;
+               goto bad_out;
+       }
+       if (a->non_resident)
+               goto do_non_resident_truncate;
+       BUG_ON(NInoNonResident(ni));
+       /* Resize the attribute record to best fit the new attribute size. */
+       if (new_size < vol->mft_record_size &&
+                       !ntfs_resident_attr_value_resize(m, a, new_size)) {
+               unsigned long flags;
+
+               /* The resize succeeded! */
+               flush_dcache_mft_record_page(ctx->ntfs_ino);
+               mark_mft_record_dirty(ctx->ntfs_ino);
+               write_lock_irqsave(&ni->size_lock, flags);
+               /* Update the sizes in the ntfs inode and all is done. */
+               ni->allocated_size = le32_to_cpu(a->length) -
+                               le16_to_cpu(a->data.resident.value_offset);
+               /*
+                * Note ntfs_resident_attr_value_resize() has already done any
+                * necessary data clearing in the attribute record.  When the
+                * file is being shrunk vmtruncate() will already have cleared
+                * the top part of the last partial page, i.e. since this is
+                * the resident case this is the page with index 0.  However,
+                * when the file is being expanded, the page cache page data
+                * between the old data_size, i.e. old_size, and the new_size
+                * has not been zeroed.  Fortunately, we do not need to zero it
+                * either since on one hand it will either already be zero due
+                * to both readpage and writepage clearing partial page data
+                * beyond i_size in which case there is nothing to do or in the
+                * case of the file being mmap()ped at the same time, POSIX
+                * specifies that the behaviour is unspecified thus we do not
+                * have to do anything.  This means that in our implementation
+                * in the rare case that the file is mmap()ped and a write
+                * occured into the mmap()ped region just beyond the file size
+                * and writepage has not yet been called to write out the page
+                * (which would clear the area beyond the file size) and we now
+                * extend the file size to incorporate this dirty region
+                * outside the file size, a write of the page would result in
+                * this data being written to disk instead of being cleared.
+                * Given both POSIX and the Linux mmap(2) man page specify that
+                * this corner case is undefined, we choose to leave it like
+                * that as this is much simpler for us as we cannot lock the
+                * relevant page now since we are holding too many ntfs locks
+                * which would result in a lock reversal deadlock.
+                */
+               ni->initialized_size = new_size;
+               write_unlock_irqrestore(&ni->size_lock, flags);
+               goto unm_done;
+       }
+       /* If the above resize failed, this must be an attribute extension. */
+       BUG_ON(size_change < 0);
+       /*
+        * We have to drop all the locks so we can call
+        * ntfs_attr_make_non_resident().  This could be optimised by try-
+        * locking the first page cache page and only if that fails dropping
+        * the locks, locking the page, and redoing all the locking and
+        * lookups.  While this would be a huge optimisation, it is not worth
+        * it as this is definitely a slow code path as it only ever can happen
+        * once for any given file.
+        */
        ntfs_attr_put_search_ctx(ctx);
-       unmap_mft_record(ni);
-       NInoClearTruncateFailed(ni);
-       ntfs_debug("Done.");
-       return 0;
-err_out:
-       if (err != -ENOMEM) {
+       unmap_mft_record(base_ni);
+       up_write(&ni->runlist.lock);
+       /*
+        * Not enough space in the mft record, try to make the attribute
+        * non-resident and if successful restart the truncation process.
+        */
+       err = ntfs_attr_make_non_resident(ni, old_size);
+       if (likely(!err))
+               goto retry_truncate;
+       /*
+        * Could not make non-resident.  If this is due to this not being
+        * permitted for this attribute type or there not being enough space,
+        * try to make other attributes non-resident.  Otherwise fail.
+        */
+       if (unlikely(err != -EPERM && err != -ENOSPC)) {
+               ntfs_error(vol->sb, "Cannot truncate inode 0x%lx, attribute "
+                               "type 0x%x, because the conversion from "
+                               "resident to non-resident attribute failed "
+                               "with error code %i.", vi->i_ino,
+                               (unsigned)le32_to_cpu(ni->type), err);
+               if (err != -ENOMEM)
+                       err = -EIO;
+               goto conv_err_out;
+       }
+       /* TODO: Not implemented from here, abort. */
+       if (err == -ENOSPC)
+               ntfs_error(vol->sb, "Not enough space in the mft record/on "
+                               "disk for the non-resident attribute value.  "
+                               "This case is not implemented yet.");
+       else /* if (err == -EPERM) */
+               ntfs_error(vol->sb, "This attribute type may not be "
+                               "non-resident.  This case is not implemented "
+                               "yet.");
+       err = -EOPNOTSUPP;
+       goto conv_err_out;
+#if 0
+       // TODO: Attempt to make other attributes non-resident.
+       if (!err)
+               goto do_resident_extend;
+       /*
+        * Both the attribute list attribute and the standard information
+        * attribute must remain in the base inode.  Thus, if this is one of
+        * these attributes, we have to try to move other attributes out into
+        * extent mft records instead.
+        */
+       if (ni->type == AT_ATTRIBUTE_LIST ||
+                       ni->type == AT_STANDARD_INFORMATION) {
+               // TODO: Attempt to move other attributes into extent mft
+               // records.
+               err = -EOPNOTSUPP;
+               if (!err)
+                       goto do_resident_extend;
+               goto err_out;
+       }
+       // TODO: Attempt to move this attribute to an extent mft record, but
+       // only if it is not already the only attribute in an mft record in
+       // which case there would be nothing to gain.
+       err = -EOPNOTSUPP;
+       if (!err)
+               goto do_resident_extend;
+       /* There is nothing we can do to make enough space. )-: */
+       goto err_out;
+#endif
+do_non_resident_truncate:
+       BUG_ON(!NInoNonResident(ni));
+       if (alloc_change < 0) {
+               highest_vcn = sle64_to_cpu(a->data.non_resident.highest_vcn);
+               if (highest_vcn > 0 &&
+                               old_alloc_size >> vol->cluster_size_bits >
+                               highest_vcn + 1) {
+                       /*
+                        * This attribute has multiple extents.  Not yet
+                        * supported.
+                        */
+                       ntfs_error(vol->sb, "Cannot truncate inode 0x%lx, "
+                                       "attribute type 0x%x, because the "
+                                       "attribute is highly fragmented (it "
+                                       "consists of multiple extents) and "
+                                       "this case is not implemented yet.",
+                                       vi->i_ino,
+                                       (unsigned)le32_to_cpu(ni->type));
+                       err = -EOPNOTSUPP;
+                       goto bad_out;
+               }
+       }
+       /*
+        * If the size is shrinking, need to reduce the initialized_size and
+        * the data_size before reducing the allocation.
+        */
+       if (size_change < 0) {
+               /*
+                * Make the valid size smaller (i_size is already up-to-date).
+                */
+               write_lock_irqsave(&ni->size_lock, flags);
+               if (new_size < ni->initialized_size) {
+                       ni->initialized_size = new_size;
+                       a->data.non_resident.initialized_size =
+                                       cpu_to_sle64(new_size);
+               }
+               a->data.non_resident.data_size = cpu_to_sle64(new_size);
+               write_unlock_irqrestore(&ni->size_lock, flags);
+               flush_dcache_mft_record_page(ctx->ntfs_ino);
+               mark_mft_record_dirty(ctx->ntfs_ino);
+               /* If the allocated size is not changing, we are done. */
+               if (!alloc_change)
+                       goto unm_done;
+               /*
+                * If the size is shrinking it makes no sense for the
+                * allocation to be growing.
+                */
+               BUG_ON(alloc_change > 0);
+       } else /* if (size_change >= 0) */ {
+               /*
+                * The file size is growing or staying the same but the
+                * allocation can be shrinking, growing or staying the same.
+                */
+               if (alloc_change > 0) {
+                       /*
+                        * We need to extend the allocation and possibly update
+                        * the data size.  If we are updating the data size,
+                        * since we are not touching the initialized_size we do
+                        * not need to worry about the actual data on disk.
+                        * And as far as the page cache is concerned, there
+                        * will be no pages beyond the old data size and any
+                        * partial region in the last page between the old and
+                        * new data size (or the end of the page if the new
+                        * data size is outside the page) does not need to be
+                        * modified as explained above for the resident
+                        * attribute truncate case.  To do this, we simply drop
+                        * the locks we hold and leave all the work to our
+                        * friendly helper ntfs_attr_extend_allocation().
+                        */
+                       ntfs_attr_put_search_ctx(ctx);
+                       unmap_mft_record(base_ni);
+                       up_write(&ni->runlist.lock);
+                       err = ntfs_attr_extend_allocation(ni, new_size,
+                                       size_change > 0 ? new_size : -1, -1);
+                       /*
+                        * ntfs_attr_extend_allocation() will have done error
+                        * output already.
+                        */
+                       goto done;
+               }
+               if (!alloc_change)
+                       goto alloc_done;
+       }
+       /* alloc_change < 0 */
+       /* Free the clusters. */
+       nr_freed = ntfs_cluster_free(ni, new_alloc_size >>
+                       vol->cluster_size_bits, -1, ctx);
+       m = ctx->mrec;
+       a = ctx->attr;
+       if (unlikely(nr_freed < 0)) {
+               ntfs_error(vol->sb, "Failed to release cluster(s) (error code "
+                               "%lli).  Unmount and run chkdsk to recover "
+                               "the lost cluster(s).", (long long)nr_freed);
                NVolSetErrors(vol);
+               nr_freed = 0;
+       }
+       /* Truncate the runlist. */
+       err = ntfs_rl_truncate_nolock(vol, &ni->runlist,
+                       new_alloc_size >> vol->cluster_size_bits);
+       /*
+        * If the runlist truncation failed and/or the search context is no
+        * longer valid, we cannot resize the attribute record or build the
+        * mapping pairs array thus we mark the inode bad so that no access to
+        * the freed clusters can happen.
+        */
+       if (unlikely(err || IS_ERR(m))) {
+               ntfs_error(vol->sb, "Failed to %s (error code %li).%s",
+                               IS_ERR(m) ?
+                               "restore attribute search context" :
+                               "truncate attribute runlist",
+                               IS_ERR(m) ? PTR_ERR(m) : err, es);
+               err = -EIO;
+               goto bad_out;
+       }
+       /* Get the size for the shrunk mapping pairs array for the runlist. */
+       mp_size = ntfs_get_size_for_mapping_pairs(vol, ni->runlist.rl, 0, -1);
+       if (unlikely(mp_size <= 0)) {
+               ntfs_error(vol->sb, "Cannot shrink allocation of inode 0x%lx, "
+                               "attribute type 0x%x, because determining the "
+                               "size for the mapping pairs failed with error "
+                               "code %i.%s", vi->i_ino,
+                               (unsigned)le32_to_cpu(ni->type), mp_size, es);
+               err = -EIO;
+               goto bad_out;
+       }
+       /*
+        * Shrink the attribute record for the new mapping pairs array.  Note,
+        * this cannot fail since we are making the attribute smaller thus by
+        * definition there is enough space to do so.
+        */
+       attr_len = le32_to_cpu(a->length);
+       err = ntfs_attr_record_resize(m, a, mp_size +
+                       le16_to_cpu(a->data.non_resident.mapping_pairs_offset));
+       BUG_ON(err);
+       /*
+        * Generate the mapping pairs array directly into the attribute record.
+        */
+       err = ntfs_mapping_pairs_build(vol, (u8*)a +
+                       le16_to_cpu(a->data.non_resident.mapping_pairs_offset),
+                       mp_size, ni->runlist.rl, 0, -1, NULL);
+       if (unlikely(err)) {
+               ntfs_error(vol->sb, "Cannot shrink allocation of inode 0x%lx, "
+                               "attribute type 0x%x, because building the "
+                               "mapping pairs failed with error code %i.%s",
+                               vi->i_ino, (unsigned)le32_to_cpu(ni->type),
+                               err, es);
+               err = -EIO;
+               goto bad_out;
+       }
+       /* Update the allocated/compressed size as well as the highest vcn. */
+       a->data.non_resident.highest_vcn = cpu_to_sle64((new_alloc_size >>
+                       vol->cluster_size_bits) - 1);
+       write_lock_irqsave(&ni->size_lock, flags);
+       ni->allocated_size = new_alloc_size;
+       a->data.non_resident.allocated_size = cpu_to_sle64(new_alloc_size);
+       if (NInoSparse(ni) || NInoCompressed(ni)) {
+               if (nr_freed) {
+                       ni->itype.compressed.size -= nr_freed <<
+                                       vol->cluster_size_bits;
+                       BUG_ON(ni->itype.compressed.size < 0);
+                       a->data.non_resident.compressed_size = cpu_to_sle64(
+                                       ni->itype.compressed.size);
+                       vi->i_blocks = ni->itype.compressed.size >> 9;
+               }
+       } else
+               vi->i_blocks = new_alloc_size >> 9;
+       write_unlock_irqrestore(&ni->size_lock, flags);
+       /*
+        * We have shrunk the allocation.  If this is a shrinking truncate we
+        * have already dealt with the initialized_size and the data_size above
+        * and we are done.  If the truncate is only changing the allocation
+        * and not the data_size, we are also done.  If this is an extending
+        * truncate, need to extend the data_size now which is ensured by the
+        * fact that @size_change is positive.
+        */
+alloc_done:
+       /*
+        * If the size is growing, need to update it now.  If it is shrinking,
+        * we have already updated it above (before the allocation change).
+        */
+       if (size_change > 0)
+               a->data.non_resident.data_size = cpu_to_sle64(new_size);
+       /* Ensure the modified mft record is written out. */
+       flush_dcache_mft_record_page(ctx->ntfs_ino);
+       mark_mft_record_dirty(ctx->ntfs_ino);
+unm_done:
+       ntfs_attr_put_search_ctx(ctx);
+       unmap_mft_record(base_ni);
+       up_write(&ni->runlist.lock);
+done:
+       /* Update the mtime and ctime on the base inode. */
+       inode_update_time(VFS_I(base_ni), 1);
+       if (likely(!err)) {
+               NInoClearTruncateFailed(ni);
+               ntfs_debug("Done.");
+       }
+       return err;
+old_bad_out:
+       old_size = -1;
+bad_out:
+       if (err != -ENOMEM && err != -EOPNOTSUPP) {
                make_bad_inode(vi);
+               make_bad_inode(VFS_I(base_ni));
+               NVolSetErrors(vol);
        }
+       if (err != -EOPNOTSUPP)
+               NInoSetTruncateFailed(ni);
+       else if (old_size >= 0)
+               i_size_write(vi, old_size);
+err_out:
        if (ctx)
                ntfs_attr_put_search_ctx(ctx);
        if (m)
-               unmap_mft_record(ni);
-       NInoSetTruncateFailed(ni);
+               unmap_mft_record(base_ni);
+       up_write(&ni->runlist.lock);
+out:
+       ntfs_debug("Failed.  Returning error code %i.", err);
        return err;
+conv_err_out:
+       if (err != -ENOMEM && err != -EOPNOTSUPP) {
+               make_bad_inode(vi);
+               make_bad_inode(VFS_I(base_ni));
+               NVolSetErrors(vol);
+       }
+       if (err != -EOPNOTSUPP)
+               NInoSetTruncateFailed(ni);
+       else
+               i_size_write(vi, old_size);
+       goto out;
 }
 
 /**
@@ -2420,8 +2845,7 @@ int ntfs_setattr(struct dentry *dentry, struct iattr *attr)
 
        err = inode_change_ok(vi, attr);
        if (err)
-               return err;
-
+               goto out;
        /* We do not support NTFS ACLs yet. */
        if (ia_valid & (ATTR_UID | ATTR_GID | ATTR_MODE)) {
                ntfs_warning(vi->i_sb, "Changes in user/group/mode are not "
@@ -2429,14 +2853,22 @@ int ntfs_setattr(struct dentry *dentry, struct iattr *attr)
                err = -EOPNOTSUPP;
                goto out;
        }
-
        if (ia_valid & ATTR_SIZE) {
                if (attr->ia_size != i_size_read(vi)) {
-                       ntfs_warning(vi->i_sb, "Changes in inode size are not "
-                                       "supported yet, ignoring.");
-                       err = -EOPNOTSUPP;
-                       // TODO: Implement...
-                       // err = vmtruncate(vi, attr->ia_size);
+                       ntfs_inode *ni = NTFS_I(vi);
+                       /*
+                        * FIXME: For now we do not support resizing of
+                        * compressed or encrypted files yet.
+                        */
+                       if (NInoCompressed(ni) || NInoEncrypted(ni)) {
+                               ntfs_warning(vi->i_sb, "Changes in inode size "
+                                               "are not supported yet for "
+                                               "%s files, ignoring.",
+                                               NInoCompressed(ni) ?
+                                               "compressed" : "encrypted");
+                               err = -EOPNOTSUPP;
+                       } else
+                               err = vmtruncate(vi, attr->ia_size);
                        if (err || ia_valid == ATTR_SIZE)
                                goto out;
                } else {
index 5c248d404f05432277be8b0d9fb147ff549578b3..f5678d5d7919029edcdfc758a28ca730afb2d2a5 100644 (file)
@@ -1021,10 +1021,17 @@ enum {
        FILE_NAME_POSIX         = 0x00,
        /* This is the largest namespace. It is case sensitive and allows all
           Unicode characters except for: '\0' and '/'.  Beware that in
-          WinNT/2k files which eg have the same name except for their case
-          will not be distinguished by the standard utilities and thus a "del
-          filename" will delete both "filename" and "fileName" without
-          warning. */
+          WinNT/2k/2003 by default files which eg have the same name except
+          for their case will not be distinguished by the standard utilities
+          and thus a "del filename" will delete both "filename" and "fileName"
+          without warning.  However if for example Services For Unix (SFU) are
+          installed and the case sensitive option was enabled at installation
+          time, then you can create/access/delete such files.
+          Note that even SFU places restrictions on the filenames beyond the
+          '\0' and '/' and in particular the following set of characters is
+          not allowed: '"', '/', '<', '>', '\'.  All other characters,
+          including the ones no allowed in WIN32 namespace are allowed.
+          Tested with SFU 3.5 (this is now free) running on Windows XP. */
        FILE_NAME_WIN32         = 0x01,
        /* The standard WinNT/2k NTFS long filenames. Case insensitive.  All
           Unicode chars except: '\0', '"', '*', '/', ':', '<', '>', '?', '\',
@@ -2367,7 +2374,9 @@ typedef struct {
  * Extended attribute flags (8-bit).
  */
 enum {
-       NEED_EA = 0x80
+       NEED_EA = 0x80          /* If set the file to which the EA belongs
+                                  cannot be interpreted without understanding
+                                  the associates extended attributes. */
 } __attribute__ ((__packed__));
 
 typedef u8 EA_FLAGS;
@@ -2375,20 +2384,20 @@ typedef u8 EA_FLAGS;
 /*
  * Attribute: Extended attribute (EA) (0xe0).
  *
- * NOTE: Always non-resident. (Is this true?)
+ * NOTE: Can be resident or non-resident.
  *
  * Like the attribute list and the index buffer list, the EA attribute value is
  * a sequence of EA_ATTR variable length records.
- *
- * FIXME: It appears weird that the EA name is not unicode. Is it true?
  */
 typedef struct {
        le32 next_entry_offset; /* Offset to the next EA_ATTR. */
        EA_FLAGS flags;         /* Flags describing the EA. */
-       u8 ea_name_length;      /* Length of the name of the EA in bytes. */
+       u8 ea_name_length;      /* Length of the name of the EA in bytes
+                                  excluding the '\0' byte terminator. */
        le16 ea_value_length;   /* Byte size of the EA's value. */
-       u8 ea_name[0];          /* Name of the EA. */
-       u8 ea_value[0];         /* The value of the EA. Immediately follows
+       u8 ea_name[0];          /* Name of the EA.  Note this is ASCII, not
+                                  Unicode and it is zero terminated. */
+       u8 ea_value[0];         /* The value of the EA.  Immediately follows
                                   the name. */
 } __attribute__ ((__packed__)) EA_ATTR;
 
index 5af3bf0b7eee15220a26841ad0812c6766720f38..29cabf93d2d24ed66ffd0aff3a5478163e93f210 100644 (file)
@@ -76,6 +76,7 @@ int ntfs_cluster_free_from_rl_nolock(ntfs_volume *vol,
  * @count:     number of clusters to allocate
  * @start_lcn: starting lcn at which to allocate the clusters (or -1 if none)
  * @zone:      zone from which to allocate the clusters
+ * @is_extension:      if TRUE, this is an attribute extension
  *
  * Allocate @count clusters preferably starting at cluster @start_lcn or at the
  * current allocator position if @start_lcn is -1, on the mounted ntfs volume
@@ -86,6 +87,13 @@ int ntfs_cluster_free_from_rl_nolock(ntfs_volume *vol,
  * @start_vcn specifies the vcn of the first allocated cluster.  This makes
  * merging the resulting runlist with the old runlist easier.
  *
+ * If @is_extension is TRUE, the caller is allocating clusters to extend an
+ * attribute and if it is FALSE, the caller is allocating clusters to fill a
+ * hole in an attribute.  Practically the difference is that if @is_extension
+ * is TRUE the returned runlist will be terminated with LCN_ENOENT and if
+ * @is_extension is FALSE the runlist will be terminated with
+ * LCN_RL_NOT_MAPPED.
+ *
  * You need to check the return value with IS_ERR().  If this is false, the
  * function was successful and the return value is a runlist describing the
  * allocated cluster(s).  If IS_ERR() is true, the function failed and
@@ -137,7 +145,8 @@ int ntfs_cluster_free_from_rl_nolock(ntfs_volume *vol,
  */
 runlist_element *ntfs_cluster_alloc(ntfs_volume *vol, const VCN start_vcn,
                const s64 count, const LCN start_lcn,
-               const NTFS_CLUSTER_ALLOCATION_ZONES zone)
+               const NTFS_CLUSTER_ALLOCATION_ZONES zone,
+               const BOOL is_extension)
 {
        LCN zone_start, zone_end, bmp_pos, bmp_initial_pos, last_read_pos, lcn;
        LCN prev_lcn = 0, prev_run_len = 0, mft_zone_size;
@@ -310,7 +319,7 @@ runlist_element *ntfs_cluster_alloc(ntfs_volume *vol, const VCN start_vcn,
                                continue;
                        }
                        bit = 1 << (lcn & 7);
-                       ntfs_debug("bit %i.", bit);
+                       ntfs_debug("bit 0x%x.", bit);
                        /* If the bit is already set, go onto the next one. */
                        if (*byte & bit) {
                                lcn++;
@@ -729,7 +738,7 @@ out:
        /* Add runlist terminator element. */
        if (likely(rl)) {
                rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length;
-               rl[rlpos].lcn = LCN_RL_NOT_MAPPED;
+               rl[rlpos].lcn = is_extension ? LCN_ENOENT : LCN_RL_NOT_MAPPED;
                rl[rlpos].length = 0;
        }
        if (likely(page && !IS_ERR(page))) {
@@ -782,6 +791,7 @@ out:
  * @ni:                ntfs inode whose runlist describes the clusters to free
  * @start_vcn: vcn in the runlist of @ni at which to start freeing clusters
  * @count:     number of clusters to free or -1 for all clusters
+ * @ctx:       active attribute search context if present or NULL if not
  * @is_rollback:       true if this is a rollback operation
  *
  * Free @count clusters starting at the cluster @start_vcn in the runlist
@@ -791,15 +801,39 @@ out:
  * deallocated.  Thus, to completely free all clusters in a runlist, use
  * @start_vcn = 0 and @count = -1.
  *
+ * If @ctx is specified, it is an active search context of @ni and its base mft
+ * record.  This is needed when __ntfs_cluster_free() encounters unmapped
+ * runlist fragments and allows their mapping.  If you do not have the mft
+ * record mapped, you can specify @ctx as NULL and __ntfs_cluster_free() will
+ * perform the necessary mapping and unmapping.
+ *
+ * Note, __ntfs_cluster_free() saves the state of @ctx on entry and restores it
+ * before returning.  Thus, @ctx will be left pointing to the same attribute on
+ * return as on entry.  However, the actual pointers in @ctx may point to
+ * different memory locations on return, so you must remember to reset any
+ * cached pointers from the @ctx, i.e. after the call to __ntfs_cluster_free(),
+ * you will probably want to do:
+ *     m = ctx->mrec;
+ *     a = ctx->attr;
+ * Assuming you cache ctx->attr in a variable @a of type ATTR_RECORD * and that
+ * you cache ctx->mrec in a variable @m of type MFT_RECORD *.
+ *
  * @is_rollback should always be FALSE, it is for internal use to rollback
  * errors.  You probably want to use ntfs_cluster_free() instead.
  *
- * Note, ntfs_cluster_free() does not modify the runlist at all, so the caller
- * has to deal with it later.
+ * Note, __ntfs_cluster_free() does not modify the runlist, so you have to
+ * remove from the runlist or mark sparse the freed runs later.
  *
  * Return the number of deallocated clusters (not counting sparse ones) on
  * success and -errno on error.
  *
+ * WARNING: If @ctx is supplied, regardless of whether success or failure is
+ *         returned, you need to check IS_ERR(@ctx->mrec) and if TRUE the @ctx
+ *         is no longer valid, i.e. you need to either call
+ *         ntfs_attr_reinit_search_ctx() or ntfs_attr_put_search_ctx() on it.
+ *         In that case PTR_ERR(@ctx->mrec) will give you the error code for
+ *         why the mapping of the old inode failed.
+ *
  * Locking: - The runlist described by @ni must be locked for writing on entry
  *           and is locked on return.  Note the runlist may be modified when
  *           needed runlist fragments need to be mapped.
@@ -807,9 +841,13 @@ out:
  *           on return.
  *         - This function takes the volume lcn bitmap lock for writing and
  *           modifies the bitmap contents.
+ *         - If @ctx is NULL, the base mft record of @ni must not be mapped on
+ *           entry and it will be left unmapped on return.
+ *         - If @ctx is not NULL, the base mft record must be mapped on entry
+ *           and it will be left mapped on return.
  */
 s64 __ntfs_cluster_free(ntfs_inode *ni, const VCN start_vcn, s64 count,
-               const BOOL is_rollback)
+               ntfs_attr_search_ctx *ctx, const BOOL is_rollback)
 {
        s64 delta, to_free, total_freed, real_freed;
        ntfs_volume *vol;
@@ -839,7 +877,7 @@ s64 __ntfs_cluster_free(ntfs_inode *ni, const VCN start_vcn, s64 count,
 
        total_freed = real_freed = 0;
 
-       rl = ntfs_attr_find_vcn_nolock(ni, start_vcn, TRUE);
+       rl = ntfs_attr_find_vcn_nolock(ni, start_vcn, ctx);
        if (IS_ERR(rl)) {
                if (!is_rollback)
                        ntfs_error(vol->sb, "Failed to find first runlist "
@@ -893,7 +931,7 @@ s64 __ntfs_cluster_free(ntfs_inode *ni, const VCN start_vcn, s64 count,
 
                        /* Attempt to map runlist. */
                        vcn = rl->vcn;
-                       rl = ntfs_attr_find_vcn_nolock(ni, vcn, TRUE);
+                       rl = ntfs_attr_find_vcn_nolock(ni, vcn, ctx);
                        if (IS_ERR(rl)) {
                                err = PTR_ERR(rl);
                                if (!is_rollback)
@@ -961,7 +999,7 @@ err_out:
         * If rollback fails, set the volume errors flag, emit an error
         * message, and return the error code.
         */
-       delta = __ntfs_cluster_free(ni, start_vcn, total_freed, TRUE);
+       delta = __ntfs_cluster_free(ni, start_vcn, total_freed, ctx, TRUE);
        if (delta < 0) {
                ntfs_error(vol->sb, "Failed to rollback (error %i).  Leaving "
                                "inconsistent metadata!  Unmount and run "
index a6a8827882e73c71705ebf9ec174805e0a75dc8e..72cbca7003b2de3ee54ffe4a358efac9e767f090 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <linux/fs.h>
 
+#include "attrib.h"
 #include "types.h"
 #include "inode.h"
 #include "runlist.h"
@@ -41,16 +42,18 @@ typedef enum {
 
 extern runlist_element *ntfs_cluster_alloc(ntfs_volume *vol,
                const VCN start_vcn, const s64 count, const LCN start_lcn,
-               const NTFS_CLUSTER_ALLOCATION_ZONES zone);
+               const NTFS_CLUSTER_ALLOCATION_ZONES zone,
+               const BOOL is_extension);
 
 extern s64 __ntfs_cluster_free(ntfs_inode *ni, const VCN start_vcn,
-               s64 count, const BOOL is_rollback);
+               s64 count, ntfs_attr_search_ctx *ctx, const BOOL is_rollback);
 
 /**
  * ntfs_cluster_free - free clusters on an ntfs volume
  * @ni:                ntfs inode whose runlist describes the clusters to free
  * @start_vcn: vcn in the runlist of @ni at which to start freeing clusters
  * @count:     number of clusters to free or -1 for all clusters
+ * @ctx:       active attribute search context if present or NULL if not
  *
  * Free @count clusters starting at the cluster @start_vcn in the runlist
  * described by the ntfs inode @ni.
@@ -59,12 +62,36 @@ extern s64 __ntfs_cluster_free(ntfs_inode *ni, const VCN start_vcn,
  * deallocated.  Thus, to completely free all clusters in a runlist, use
  * @start_vcn = 0 and @count = -1.
  *
- * Note, ntfs_cluster_free() does not modify the runlist at all, so the caller
- * has to deal with it later.
+ * If @ctx is specified, it is an active search context of @ni and its base mft
+ * record.  This is needed when ntfs_cluster_free() encounters unmapped runlist
+ * fragments and allows their mapping.  If you do not have the mft record
+ * mapped, you can specify @ctx as NULL and ntfs_cluster_free() will perform
+ * the necessary mapping and unmapping.
+ *
+ * Note, ntfs_cluster_free() saves the state of @ctx on entry and restores it
+ * before returning.  Thus, @ctx will be left pointing to the same attribute on
+ * return as on entry.  However, the actual pointers in @ctx may point to
+ * different memory locations on return, so you must remember to reset any
+ * cached pointers from the @ctx, i.e. after the call to ntfs_cluster_free(),
+ * you will probably want to do:
+ *     m = ctx->mrec;
+ *     a = ctx->attr;
+ * Assuming you cache ctx->attr in a variable @a of type ATTR_RECORD * and that
+ * you cache ctx->mrec in a variable @m of type MFT_RECORD *.
+ *
+ * Note, ntfs_cluster_free() does not modify the runlist, so you have to remove
+ * from the runlist or mark sparse the freed runs later.
  *
  * Return the number of deallocated clusters (not counting sparse ones) on
  * success and -errno on error.
  *
+ * WARNING: If @ctx is supplied, regardless of whether success or failure is
+ *         returned, you need to check IS_ERR(@ctx->mrec) and if TRUE the @ctx
+ *         is no longer valid, i.e. you need to either call
+ *         ntfs_attr_reinit_search_ctx() or ntfs_attr_put_search_ctx() on it.
+ *         In that case PTR_ERR(@ctx->mrec) will give you the error code for
+ *         why the mapping of the old inode failed.
+ *
  * Locking: - The runlist described by @ni must be locked for writing on entry
  *           and is locked on return.  Note the runlist may be modified when
  *           needed runlist fragments need to be mapped.
@@ -72,11 +99,15 @@ extern s64 __ntfs_cluster_free(ntfs_inode *ni, const VCN start_vcn,
  *           on return.
  *         - This function takes the volume lcn bitmap lock for writing and
  *           modifies the bitmap contents.
+ *         - If @ctx is NULL, the base mft record of @ni must not be mapped on
+ *           entry and it will be left unmapped on return.
+ *         - If @ctx is not NULL, the base mft record must be mapped on entry
+ *           and it will be left mapped on return.
  */
 static inline s64 ntfs_cluster_free(ntfs_inode *ni, const VCN start_vcn,
-               s64 count)
+               s64 count, ntfs_attr_search_ctx *ctx)
 {
-       return __ntfs_cluster_free(ni, start_vcn, count, FALSE);
+       return __ntfs_cluster_free(ni, start_vcn, count, ctx, FALSE);
 }
 
 extern int ntfs_cluster_free_from_rl_nolock(ntfs_volume *vol,
index 590887b943f511ca964bc1c4cd30dc4cf7e0b0fd..e38e402e410351c6a70ff9ae148f10133ee28177 100644 (file)
@@ -39,8 +39,7 @@
  * If there was insufficient memory to complete the request, return NULL.
  * Depending on @gfp_mask the allocation may be guaranteed to succeed.
  */
-static inline void *__ntfs_malloc(unsigned long size,
-               gfp_t gfp_mask)
+static inline void *__ntfs_malloc(unsigned long size, gfp_t gfp_mask)
 {
        if (likely(size <= PAGE_SIZE)) {
                BUG_ON(!size);
index b011369b59561139687e627f211207dc4edff72e..0c65cbb8c5cf675af8eba7b682fac412cfa27eee 100644 (file)
@@ -49,7 +49,8 @@ static inline MFT_RECORD *map_mft_record_page(ntfs_inode *ni)
        ntfs_volume *vol = ni->vol;
        struct inode *mft_vi = vol->mft_ino;
        struct page *page;
-       unsigned long index, ofs, end_index;
+       unsigned long index, end_index;
+       unsigned ofs;
 
        BUG_ON(ni->page);
        /*
@@ -1308,7 +1309,7 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(ntfs_volume *vol)
        ll = mftbmp_ni->allocated_size;
        read_unlock_irqrestore(&mftbmp_ni->size_lock, flags);
        rl = ntfs_attr_find_vcn_nolock(mftbmp_ni,
-                       (ll - 1) >> vol->cluster_size_bits, TRUE);
+                       (ll - 1) >> vol->cluster_size_bits, NULL);
        if (unlikely(IS_ERR(rl) || !rl->length || rl->lcn < 0)) {
                up_write(&mftbmp_ni->runlist.lock);
                ntfs_error(vol->sb, "Failed to determine last allocated "
@@ -1354,7 +1355,8 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(ntfs_volume *vol)
                up_write(&vol->lcnbmp_lock);
                ntfs_unmap_page(page);
                /* Allocate a cluster from the DATA_ZONE. */
-               rl2 = ntfs_cluster_alloc(vol, rl[1].vcn, 1, lcn, DATA_ZONE);
+               rl2 = ntfs_cluster_alloc(vol, rl[1].vcn, 1, lcn, DATA_ZONE,
+                               TRUE);
                if (IS_ERR(rl2)) {
                        up_write(&mftbmp_ni->runlist.lock);
                        ntfs_error(vol->sb, "Failed to allocate a cluster for "
@@ -1738,7 +1740,7 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
        ll = mft_ni->allocated_size;
        read_unlock_irqrestore(&mft_ni->size_lock, flags);
        rl = ntfs_attr_find_vcn_nolock(mft_ni,
-                       (ll - 1) >> vol->cluster_size_bits, TRUE);
+                       (ll - 1) >> vol->cluster_size_bits, NULL);
        if (unlikely(IS_ERR(rl) || !rl->length || rl->lcn < 0)) {
                up_write(&mft_ni->runlist.lock);
                ntfs_error(vol->sb, "Failed to determine last allocated "
@@ -1779,7 +1781,8 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
                        nr > min_nr ? "default" : "minimal", (long long)nr);
        old_last_vcn = rl[1].vcn;
        do {
-               rl2 = ntfs_cluster_alloc(vol, old_last_vcn, nr, lcn, MFT_ZONE);
+               rl2 = ntfs_cluster_alloc(vol, old_last_vcn, nr, lcn, MFT_ZONE,
+                               TRUE);
                if (likely(!IS_ERR(rl2)))
                        break;
                if (PTR_ERR(rl2) != -ENOSPC || nr == min_nr) {
@@ -1951,20 +1954,21 @@ restore_undo_alloc:
                NVolSetErrors(vol);
                return ret;
        }
-       a = ctx->attr;
-       a->data.non_resident.highest_vcn = cpu_to_sle64(old_last_vcn - 1);
+       ctx->attr->data.non_resident.highest_vcn =
+                       cpu_to_sle64(old_last_vcn - 1);
 undo_alloc:
-       if (ntfs_cluster_free(mft_ni, old_last_vcn, -1) < 0) {
+       if (ntfs_cluster_free(mft_ni, old_last_vcn, -1, ctx) < 0) {
                ntfs_error(vol->sb, "Failed to free clusters from mft data "
                                "attribute.%s", es);
                NVolSetErrors(vol);
        }
+       a = ctx->attr;
        if (ntfs_rl_truncate_nolock(vol, &mft_ni->runlist, old_last_vcn)) {
                ntfs_error(vol->sb, "Failed to truncate mft data attribute "
                                "runlist.%s", es);
                NVolSetErrors(vol);
        }
-       if (mp_rebuilt) {
+       if (mp_rebuilt && !IS_ERR(ctx->mrec)) {
                if (ntfs_mapping_pairs_build(vol, (u8*)a + le16_to_cpu(
                                a->data.non_resident.mapping_pairs_offset),
                                old_alen - le16_to_cpu(
@@ -1981,6 +1985,10 @@ undo_alloc:
                }
                flush_dcache_mft_record_page(ctx->ntfs_ino);
                mark_mft_record_dirty(ctx->ntfs_ino);
+       } else if (IS_ERR(ctx->mrec)) {
+               ntfs_error(vol->sb, "Failed to restore attribute search "
+                               "context.%s", es);
+               NVolSetErrors(vol);
        }
        if (ctx)
                ntfs_attr_put_search_ctx(ctx);
index 453d0d51ea4bc4d9b731e48345db0e45bb034b24..6c16db9e1a8a6ba2a6ce1e26f9d891f31218e7c8 100644 (file)
@@ -1447,7 +1447,7 @@ not_enabled:
        if (unlikely(i_size_read(tmp_ino) < sizeof(USN_HEADER))) {
                ntfs_error(vol->sb, "Found corrupt $UsnJrnl/$DATA/$Max "
                                "attribute (size is 0x%llx but should be at "
-                               "least 0x%x bytes).", i_size_read(tmp_ino),
+                               "least 0x%zx bytes).", i_size_read(tmp_ino),
                                sizeof(USN_HEADER));
                return FALSE;
        }
index a515e2aed829212a76fcd462bb775a9fdc8a13dd..8c454aa58ac6b157441ad2a77f9211e4a30f882e 100644 (file)
@@ -118,8 +118,7 @@ extern void release_lapic_nmi(void);
 extern void disable_timer_nmi_watchdog(void);
 extern void enable_timer_nmi_watchdog(void);
 extern void nmi_watchdog_tick (struct pt_regs * regs);
-extern int APIC_init(void);
-extern void APIC_late_time_init(void);
+extern int APIC_init_uniprocessor (void);
 extern void disable_APIC_timer(void);
 extern void enable_APIC_timer(void);
 
index 9139b89497a1aa494b63203179360124a1c3c983..622815bf3243e00306774bbea1ab01e2bc2a2f3e 100644 (file)
@@ -55,7 +55,6 @@ void init_8259A(int aeoi);
 void FASTCALL(send_IPI_self(int vector));
 void init_VISWS_APIC_irqs(void);
 void setup_IO_APIC(void);
-void IO_APIC_late_time_init(void);
 void disable_IO_APIC(void);
 void print_IO_APIC(void);
 int IO_APIC_get_PCI_irq_vector(int bus, int slot, int fn);
index d7c70c144f9fa16969286257818063329d7bf5cf..7f45f6311059f5d8e3675dbf6d0e759407ad72fb 100644 (file)
@@ -1,6 +1,11 @@
 /* two abstractions specific to kernel/smpboot.c, mainly to cater to visws
  * which needs to alter them. */
 
+static inline void smpboot_clear_io_apic_irqs(void)
+{
+       io_apic_irqs = 0;
+}
+
 static inline void smpboot_setup_warm_reset_vector(unsigned long start_eip)
 {
        CMOS_WRITE(0xa, 0xf);
@@ -27,3 +32,13 @@ static inline void smpboot_restore_warm_reset_vector(void)
 
        *((volatile long *) phys_to_virt(0x467)) = 0;
 }
+
+static inline void smpboot_setup_io_apic(void)
+{
+       /*
+        * Here we can be sure that there is an IO-APIC in the system. Let's
+        * go and set it up:
+        */
+       if (!skip_ioapic_setup && nr_ioapics)
+               setup_IO_APIC();
+}
index 14d8e0375f7ab5c408a7702944146b4759c5543a..d926471fa3597215d07817defd8866602f0cd7d3 100644 (file)
@@ -11,7 +11,14 @@ static inline void smpboot_setup_warm_reset_vector(unsigned long start_eip)
 
 /* for visws do nothing for any of these */
 
+static inline void smpboot_clear_io_apic_irqs(void)
+{
+}
+
 static inline void smpboot_restore_warm_reset_vector(void)
 {
 }
 
+static inline void smpboot_setup_io_apic(void)
+{
+}
index 549f44843c5ebfbb59e8a8b7d98ff5522881524d..bba5305c29ed72f6c9407be3373183f030ecd569 100644 (file)
@@ -18,7 +18,7 @@
 #define __ASM_PPC_SYS_H
 
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/types.h>
 
 #if defined(CONFIG_8260)
index a9e72ac3fb9fd066ebc5607bd28cfdd4ba8f010e..17cbc6db67b48390cb823916eb33bf197ff8c751 100644 (file)
@@ -396,32 +396,6 @@ extern struct device * get_device(struct device * dev);
 extern void put_device(struct device * dev);
 
 
-/* drivers/base/platform.c */
-
-struct platform_device {
-       const char      * name;
-       u32             id;
-       struct device   dev;
-       u32             num_resources;
-       struct resource * resource;
-};
-
-#define to_platform_device(x) container_of((x), struct platform_device, dev)
-
-extern int platform_device_register(struct platform_device *);
-extern void platform_device_unregister(struct platform_device *);
-
-extern struct bus_type platform_bus_type;
-extern struct device platform_bus;
-
-extern struct resource *platform_get_resource(struct platform_device *, unsigned int, unsigned int);
-extern int platform_get_irq(struct platform_device *, unsigned int);
-extern struct resource *platform_get_resource_byname(struct platform_device *, unsigned int, char *);
-extern int platform_get_irq_byname(struct platform_device *, char *);
-extern int platform_add_devices(struct platform_device **, int);
-
-extern struct platform_device *platform_device_register_simple(char *, unsigned int, struct resource *, unsigned int);
-
 /* drivers/base/power.c */
 extern void device_shutdown(void);
 
diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h
new file mode 100644 (file)
index 0000000..a726225
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * platform_device.h - generic, centralized driver model
+ *
+ * Copyright (c) 2001-2003 Patrick Mochel <mochel@osdl.org>
+ *
+ * This file is released under the GPLv2
+ *
+ * See Documentation/driver-model/ for more information.
+ */
+
+#ifndef _PLATFORM_DEVICE_H_
+#define _PLATFORM_DEVICE_H_
+
+#include <linux/device.h>
+
+struct platform_device {
+       const char      * name;
+       u32             id;
+       struct device   dev;
+       u32             num_resources;
+       struct resource * resource;
+};
+
+#define to_platform_device(x) container_of((x), struct platform_device, dev)
+
+extern int platform_device_register(struct platform_device *);
+extern void platform_device_unregister(struct platform_device *);
+
+extern struct bus_type platform_bus_type;
+extern struct device platform_bus;
+
+extern struct resource *platform_get_resource(struct platform_device *, unsigned int, unsigned int);
+extern int platform_get_irq(struct platform_device *, unsigned int);
+extern struct resource *platform_get_resource_byname(struct platform_device *, unsigned int, char *);
+extern int platform_get_irq_byname(struct platform_device *, char *);
+extern int platform_add_devices(struct platform_device **, int);
+
+extern struct platform_device *platform_device_register_simple(char *, unsigned int, struct resource *, unsigned int);
+
+#endif /* _PLATFORM_DEVICE_H_ */
index 317a979b24de027c572683d458f7020838901783..2b799d40d66901948b07b5cd1263e6acaed06163 100644 (file)
@@ -12,7 +12,7 @@
 #define _LINUX_SERIAL_8250_H
 
 #include <linux/serial_core.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 /*
  * This is the platform device platform_data structure
index 14cb2718cb77f1a0d00865ee8ff09e8456ae4fef..46e3c0bf3c946bc0cead53cfdd8cb12ae9d791c4 100644 (file)
@@ -1055,6 +1055,7 @@ typedef struct {
        unsigned char emu10k2_chip; /* Audigy 1 or Audigy 2. */
        unsigned char ca0102_chip;  /* Audigy 1 or Audigy 2. Not SB Audigy 2 Value. */
        unsigned char ca0108_chip;  /* Audigy 2 Value */
+       unsigned char ca_cardbus_chip; /* Audigy 2 ZS Notebook */
        unsigned char ca0151_chip;  /* P16V */
        unsigned char spk71;        /* Has 7.1 speakers */
        unsigned char sblive51;     /* SBLive! 5.1 - extout 0x11 -> center, 0x12 -> lfe */
index 4075d97e94b184c4d8b95a1e95057e249cf45f21..f142d403534190f4588b3d7eda585112077e0607 100644 (file)
 #endif
 #endif
 
+#ifdef CONFIG_X86_LOCAL_APIC
+#include <asm/smp.h>
+#endif
+
 /*
  * Versions of gcc older than that listed below may actually compile
  * and link okay, but the end product can have subtle run time bugs.
@@ -310,7 +314,14 @@ extern void setup_arch(char **);
 
 #ifndef CONFIG_SMP
 
+#ifdef CONFIG_X86_LOCAL_APIC
+static void __init smp_init(void)
+{
+       APIC_init_uniprocessor();
+}
+#else
 #define smp_init()     do { } while (0)
+#endif
 
 static inline void setup_per_cpu_areas(void) { }
 static inline void smp_prepare_cpus(unsigned int maxcpus) { }
index 96387e20184ab85971794daeb524ed07de6f8e18..154ae13d8b7e33bd9f5d929b2c7e4c16ab38590c 100644 (file)
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -259,6 +259,8 @@ void __pagevec_release(struct pagevec *pvec)
        pagevec_reinit(pvec);
 }
 
+EXPORT_SYMBOL(__pagevec_release);
+
 /*
  * pagevec_release() for pages which are known to not be on the LRU
  *
@@ -387,6 +389,7 @@ unsigned pagevec_lookup_tag(struct pagevec *pvec, struct address_space *mapping,
        return pagevec_count(pvec);
 }
 
+EXPORT_SYMBOL(pagevec_lookup_tag);
 
 #ifdef CONFIG_SMP
 /*
index 877bb00d3295d77dccc221718753532b0783ad74..d1f9da498729c96040a477311e7c67cc94d3fc99 100644 (file)
@@ -13,7 +13,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/interrupt.h>
 #include <linux/wait.h>
 #include <linux/delay.h>
index 59202de1d2ce3a236f47664e8d806762a2bc7eb8..41e224986f35e7306e5a8fda52bdf91a0860c81c 100644 (file)
@@ -28,6 +28,8 @@
 #include <linux/ctype.h>
 #include <linux/pci.h>
 #include <linux/pm.h>
+#include <linux/platform_device.h>
+
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/info.h>
index e9cd8e054f25cea7d7fccdbe5b8a2bfae65dc5dc..53aeff0b783ad059a706c84c134994723f1a614e 100644 (file)
@@ -579,6 +579,30 @@ static int __devinit snd_emu10k1_ecard_init(emu10k1_t * emu)
        return 0;
 }
 
+static int __devinit snd_emu10k1_cardbus_init(emu10k1_t * emu)
+{
+       unsigned long special_port;
+       unsigned int value;
+
+       /* Special initialisation routine
+        * before the rest of the IO-Ports become active.
+        */
+       special_port = emu->port + 0x38;
+       value = inl(special_port);
+       outl(0x00d00000, special_port);
+       value = inl(special_port);
+       outl(0x00d00001, special_port);
+       value = inl(special_port);
+       outl(0x00d0005f, special_port);
+       value = inl(special_port);
+       outl(0x00d0007f, special_port);
+       value = inl(special_port);
+       outl(0x0090007f, special_port);
+       value = inl(special_port);
+
+       return 0;
+}
+
 /*
  *  Create the EMU10K1 instance
  */
@@ -624,6 +648,16 @@ static emu_chip_details_t emu_chip_details[] = {
         .ca0108_chip = 1,
         .spk71 = 1,
         .ac97_chip = 1} ,
+       /* Audigy 2 ZS Notebook Cardbus card.*/
+       /* Tested by James@superbug.co.uk 30th October 2005 */
+       /* Not working yet, but progressing. */
+       {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x20011102,
+        .driver = "Audigy2", .name = "Audigy 2 ZS Notebook [SB0530]", 
+        .id = "Audigy2",
+        .emu10k2_chip = 1,
+        .ca0108_chip = 1,
+        .ca_cardbus_chip = 1,
+        .spk71 = 1} ,
        {.vendor = 0x1102, .device = 0x0008, 
         .driver = "Audigy2", .name = "Audigy 2 Value [Unknown]", 
         .id = "Audigy2",
@@ -1011,6 +1045,11 @@ int __devinit snd_emu10k1_create(snd_card_t * card,
                        snd_emu10k1_free(emu);
                        return err;
                }
+       } else if (emu->card_capabilities->ca_cardbus_chip) {
+               if ((err = snd_emu10k1_cardbus_init(emu)) < 0) {
+                       snd_emu10k1_free(emu);
+                       return err;
+               }
        } else {
                /* 5.1: Enable the additional AC97 Slots. If the emu10k1 version
                        does not support this, it shouldn't do any harm */